zoukankan      html  css  js  c++  java
  • 设计模式——工厂模式

    常说的工厂模式一般认为有三种:简单工厂、工厂方法模式、抽象工厂模式。其中简单工厂严格上来说不是一种设计模式,而被认为是一种好的编码习惯/风格。

    简单工厂

    简单工厂的本质就是封装变化的代码,使客户代码将要面临的改变变少。而且被封装的代码也有了更好的复用性,比如服务多个客户端或者被继承/包装等工具来扩展。

    下面以肾5和肾6为对象来说明

    //define product(iphone) interface
    public interface IPhone{
        public void model();    
    }
    
    //iphone5
    public class Phone5 implements IPhone{
        public void model(){
            System.out.printf("it is ipone5");
        }
    }
    
    //iphone6
    public class Phone6 implements IPhone{
        public void model(){
            System.out.printf("it is ipone6");
        }
    }
    
    
    //Client: An apple store
    public class AppleStore{
        public IPhone sellPhone(String model){
            if(model.equals("5")){
                return new Phone5();
            } else if(model.equals("6")){
                return new Phone6();
            } 
        }
    }
    
    //change it with SimpleFactory
    //Client: An apple store
    public class AppleStore{
        private PhoneFactory factory;
        public IPhone sellPhone(String model){
            return factory.createPhone(model);
        }
    }
    
    public class PhoneFactory{
        public IPhone createPhone(String model){
            if(model.equals("5")){
                return new Phone5();
            } else if(model.equals("6")){
                return new Phone6();
            } 
        }
    }

          从上面使用简单工厂的前后我们可以看出,简单工厂仅仅就是把if else那部分易变的代码,因为会不断的有新产品退出或者有旧的产品不再出售,进行了封装。但是这样的改变带来的优点也是很明显的。比如前面提到的服务多个客户端,假如还有一个仓库管理系统也可以利用这个PhoneFactory来创建iphone,而不用重新写一份if else的代码;再比如,假设在美国卖的是64GB版本,在中国卖的都是128GB版本,那么可以保持客户端的代码不变,只要修改一下我们提供的接口类PhoneFactory即可达到相同信号但是不同产品的目的。也可以给AppleStore添加setFactory方法来设置不同版本的Factory以达到动态设定的效果。

          从"对修改关闭"的原则上来看,简单工厂也能使代码更贴近这种原则。采用简单工厂之后,需要修改的只是自己控制的工厂代码,而对于客户代码则不需要修改,遵循了“对修改关闭”原则。

          也有把createPhone写成静态方法的,这样的做法就可以不用创建工厂实例,但缺点就是不能通过继承来改变工厂。

    工厂模式

          工厂方法模式:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个,把类的实例化推迟到工厂子类中。

          下面我们看一个具体的实例,使用工厂方法模式来重写上面的卖手机模型。

     1 //change it with FactoryMode
     2 //Client: An apple store
     3 public class AppleStore{
     4     private PhoneFactory factory;
     5 
     6     public AppleStore(PhoneFactory factoy){
     7         this.factory = factory;
     8     }
     9 
    10     public IPhone sellPhone(String model){
    11         return factory.createPhone(model);
    12     }
    13 }
    14 
    15 //factory interface
    16 public abstract class PhoneFactory{
    17     public final IPhone createPhone(String model){
    18         return wrap(assemble(model));
    19     }
    20 
    21     public final void wrap(IPhone phone){
    22         System.out.printf("wrap this phone");
    23         return phone;
    24     }
    25 
    26     abstract IPhone assemble(String model);
    27 }
    28     
    29 // fatory of USA
    30 public class PhoneFactoryA extends PhoneFactory{
    31     public IPhone assemble(String model){
    32         if(model.equals("5")){
    33             return new Phone5ForA();
    34         } else if(model.equals("6")){
    35             return new Phone6ForA();
    36         } 
    37     }
    38 }
    39 
    40 //factory of CHN
    41 public class PhoneFactoryC extends PhoneFactory{
    42     public IPhone assemble(String model){
    43         if(model.equals("5")){
    44             return new Phone5ForC();
    45         } else if(model.equals("6")){
    46             return new Phone6ForC();
    47         } 
    48     }
    49 }
    50 
    51 
    52 //applestore in usa
    53 AppleStore usaStore = new AppleStore(new PhoneFactoryA());
    54 //applestore in CHN
    55 AppleStore chnStore = new AppleStore(new PhoneFactoryC());

          上面的工厂方法模型中,我们首先创建了一个工厂接口,然后分别实现了其子类USA工厂和CHN工厂,以分别制造适应不同国家的版本。Phone5ForA等对应的是不同国家的不同版本的类,代码中没有写出来。如果又要开一个HK店,那么只要实现一个HKPhoneFactoy子类即可。

          上面比较重要的一点是:写了两个final方法:创建手机和包装手机,然后从代码可以看出来,手机组装好后,必须经过wrap(包装)才可以从store中售出。我们知道final方法不可以被重写,也就是说,上面的流程:“组装-包装-出售”是不可以在子类中被更改的,这样的话,就可以防止不良工厂卖出没有包装盒的手机,即就对子类的一些行为进行了控制,这也是工厂方法模式中很重要的一点。

          工厂方法最重要的特点的就是创建工厂接口,使得能够针对接口编程,而不是针对实现编程,简单工厂就是针对实现编程。使用针对接口编程可以带来更大的弹性。工厂方法接口的子类与简单工厂中的工厂十分相似,从功能上看也是一致的,都实现了封装变化部分的效果。但是从大局上看,简单工厂仅仅就是实现封装变化部分的效果,而工厂方法模式则是搭建了一个很有弹性的框架。

    抽象工厂模式

    抽象工厂定义:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

    继续上面的例子。我们知道手机是由多个零件组装起来的,比如CPU,屏幕等,对于上面的Phone5和Phone6,组装他们的时候都需要CPU和屏幕,但是各自的具体零件型号又不相同,这时,就该我们的抽象工厂方法上场了。

    //define product(iphone) interface
    public interface IPhone{
        protected IComponentFactory componentFactory;
        public void model();    
    }
    
    //iphone5
    public class Phone5 implements IPhone{
    
        public Phone5(){
            componentFactory = new ComponentFactory5();
        }
    
        public void model(){
            System.out.printf("it is ipone5");
        }
    }
    
    //iphone6
    public class Phone6 implements IPhone{
    
        public Phone6(){
            componentFactory = new ComponentFactory6();
        }
    
        public void model(){
            System.out.printf("it is ipone6");
        }
    }
    
    //ComponentFactory interface
    public interface IComponentFactory{
        protected CPU cpu;
        protected Screen screen;
    }
    
    //ComponentFactory5
    public class ComponentFactory5 implements IComponentFactory{
        public ComponentFactory5(){
            cpu = CPUFactory.createCPU(5);
            screen = ScreenFactory.createScreen(5);
    }
    
    //ComponentFactory6
    public class ComponentFactory6 implements IComponentFactory{
        public ComponentFactory6(){
            cpu = CPUFactory.createCPU(6);
            screen = ScreenFactory.createScreen(6);
    }
    
    //cpu
    public interface CPU{
    }
    
    public class CPU5 implements CPU{
    }
    
    public class CPU6 implements CPU{
    }
    
    public class CPUFactory{
        public static CPU createCPU(int model){
            if(model == 5){
                  return new CPU5();
            } else if (model == 6){
                  return new CPU6();
            }
    }
    
    //screen
    public interface Screen{
    }
    
    public class Screen5 implements Screen{
    }
    
    public class Screen6 implements Screen{
    }
    
    public class ScreenFactory{
        public static Screen createScreen(int model){
            if(model == 5){
                  return new Screen5();
            } else if (model == 6){
                  return new Screen6();
            }
    }

          在上面的代码中,组装Phone5和Phone6都需要CPU和Screen零件,于是他们都有一个零件工厂IComponentFactory(抽象工厂接口),各自的零件工厂都指定了自己需要什么样子的零件。而零件工厂创建零件的时候,则都是分别调用的具体零件工厂CPUFactory和ScreenFactory来创建具体的零件,也即本抽象工厂的实现中也应用到了前面提到的工厂方法模型。从这里也可以发现:抽象工厂他们不真正的制造具体的对象,他们仅仅指定我们需要什么要的对象,就好比抽象工厂方法就是一张装配清单(比如大部分手机厂干的就是这个活),而工厂方法则是负责制造这个装配清单上的具体零件(比如高通、联发科等就是干的制造CPU的活),这也许是抽象工厂之所以被叫做抽象的原因。更进一步的我们可以发现,抽象工厂其实可以理解为组装工厂,它的模型就是通过组合不同的Component来实现的;而工厂方法模式则是通过继承,把具体实现交给子类来实现的。

          在上面的抽象工厂代码中,Phone类不需要关心CPU和Screen具体是怎么创建的(这些具体的创建都是CPUFactory和ScreenFactory),而只知道我需要哪种CPU和Screen,这样就把Phone从具体的CPU和Screen类解耦了。

  • 相关阅读:
    算法与数据结构——排序(五)希尔排序
    算法与数据结构——排序(四)简单插入排序
    算法与数据结构——排序(九)快速排序
    算法与数据结构——排序(六)堆排序
    [Keil51]51单片机定时器的方式0使用注意
    [转]简单的随机数加密算法实现
    [KeilC51]MCS51指令集中ret和reti的区别___待续
    [C]FILE结构体(不知其然,不知所以然)
    [KeilC51]这几天遇到的问题
    [KeilC51] keil c51编译器不支持匿名结构体
  • 原文地址:https://www.cnblogs.com/willhua/p/5298983.html
Copyright © 2011-2022 走看看