工厂方法模式

2019/5/23 posted in  算法

工厂方法模式介绍

工厂方法模式(Factory Pattern)是一种创建型设计模式,是日常开发中使用频率较高的一种设计模式。下面就要我们一起去了解一下工厂方法设计模式。

工厂方法设计模式定义

​定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使
​一个类的实例化延迟到其子类。

工厂方法设计模式使用场景

  • 工厂方法模式是new一个对象的替代品,所以在有需要生成对象的地方都可以使用,但是要慎重的考虑当前任务是不是需要增加一个工厂类进行管理,避免使自己的代码更加复杂。
  • 需要灵活、可扩展的框架时,可以考虑采用工厂方法模式。

工厂方法设计模式通用UML类图

说明

  • Product 产品类的抽象方法,由具体的产品类去实现。
  • ConcreateProduct 具体的产品类,可以有多个。继承于Product
  • Creator 抽象工厂类,负责定义产品对象的产生。
  • ConcreateCreator 具体的工厂实现类,继承于Creator。

工厂方法模式的优缺点

优点

  • 良好的封装性,代码结构清晰 不需要知道对象的创建过程,降低模块间的耦合。
  • 工厂方法模式的拓展性非常优秀 在增加产品类的情况下,只要适当修改具体的工厂类或者拓展一个工厂类,就可以拥抱变化。例如在我们的例子中需要生产一个装radHat的系统的电脑,则只需要增加一个RedHatSoftware类,工厂类不需要做任何修改就可以完成系统拓展。
  • 屏蔽产品类 产品类的任何变化,调用者都无需关心,它只要关心产品的接口。
  • 典型的解耦框架 高层模块只需要知道产品的抽象类,其他的实现类无需关心,符合迪米特原则,我们不需要的就不要去交流,符合依赖倒置原则,只依赖产品的抽象,符合里氏替换原则,使用产品子类替换产品父类,当然也没有问题。

缺点

  • 类结构的复杂化 每次为工厂方法模式添加新的产品时就要编写一个新的产品类,还要引入抽象层。

工厂方法模式使用实例

抽象产品类

public abstract class InstallSoftware {
   /**
    * 安装系统
    *
    * @param systemOs 系统
    */
   public abstract void installSystem(String systemOs);

   /**
    * 安装显卡驱动
    *
    * @param GPUType 显卡型号
    */
   public abstract void installGPUDriver(String GPUType);

   /**
    * 安装主板驱动
    *
    * @param mainBoxType 主板型号
    */
   public abstract void installMainBoxdriver(String mainBoxType);

   /**
    * 安装网卡驱动
    *
    * @param netCardType 网卡型号
    */
   public abstract void installNetCardDriver(String netCardType);

   /**
    * 安装声卡驱动
    *
    * @param audioType 声卡型号
    */
   public abstract void installAudioDriver(String audioType);

   /**
    * 安装USB驱动
    * @param usbDriverType(3.0,2.0,...)
    */
   public abstract void installUSBDriver(String usbDriverType);

   /**
    * 安装键盘驱动
    * @param keyBoardType 键盘型号
    */
   public abstract void installKeyBoardDriver(String keyBoardType);

   /**
    * 安装鼠标驱动
    * @param mouseType 鼠标型号
    */
   public abstract void installMouseDriver(String mouseType);
   /**
    * 软件系统安装完成
    * @return
    */
   public abstract String finish();
}

具体的产品Windows系统的电脑

public class WindowsSoftware extends InstallSoftware {
       @Override
       public void installSystem(String systemOs) {
           //系统下载安装处理
       }

       @Override
       public void installGPUDriver(String GPUType) {
           //显卡驱动下载安装处理
       }

       @Override
       public void installMainBoxdriver(String mainBoxType) {
           //主板驱动下载安装处理
       }

       @Override
       public void installNetCardDriver(String netCardType) {
           //网卡驱动下载安装处理
       }

       @Override
       public void installAudioDriver(String audioType) {
           //声卡驱动下载安装处理
       }

       @Override
       public void installUSBDriver(String usbDriverType) {
           //USB驱动下载安装处理
       }

       @Override
       public void installKeyBoardDriver(String keyBoardType) {
           //键盘驱动下载安装处理
       }

       @Override
       public void installMouseDriver(String mouseType) {
           //鼠标驱动下载安装处理
       }
     
       @Override
       public String finish() {
           return "You can use normal Windows PC";
       }
   }

具体的产品类,Ubuntu系统的电脑

public class UbuntuSoftware extends InstallSoftware {
   @Override
   public void installSystem(String systemOs) {
       //系统下载安装处理
   }

   @Override
   public void installGPUDriver(String GPUType) {
       //显卡驱动下载安装处理
   }

   @Override
   public void installMainBoxdriver(String mainBoxType) {
       //主板驱动下载安装处理
   }

   @Override
   public void installNetCardDriver(String netCardType) {
       //网卡驱动下载安装处理
   }

   @Override
   public void installAudioDriver(String audioType) {
       //声卡驱动下载安装处理
   }

   @Override
   public void installUSBDriver(String usbDriverType) {
       //USB驱动下载安装处理
   }

   @Override
   public void installKeyBoardDriver(String keyBoardType) {
       //键盘驱动下载安装处理
   }

   @Override
   public void installMouseDriver(String mouseType) {
       //鼠标驱动下载安装处理
   }
 
   @Override
   public String finish() {
       return "You can use normal Ubuntu PC";
   }
}

具体的产品类,Mac 系统的电脑

public class MacSoftware extends InstallSoftware {
   @Override
   public void installSystem(String systemOs) {
       //系统下载安装处理
   }

   @Override
   public void installGPUDriver(String GPUType) {
       //显卡驱动下载安装处理
   }

   @Override
   public void installMainBoxdriver(String mainBoxType) {
       //主板驱动下载安装处理
   }

   @Override
   public void installNetCardDriver(String netCardType) {
       //网卡驱动下载安装处理
   }

   @Override
   public void installAudioDriver(String audioType) {
       //声卡驱动下载安装处理
   }

   @Override
   public void installUSBDriver(String usbDriverType) {
       //USB驱动下载安装处理
   }

   @Override
   public void installKeyBoardDriver(String keyBoardType) {
       //键盘驱动下载安装处理
   }

   @Override
   public void installMouseDriver(String mouseType) {
       //鼠标驱动下载安装处理
   }
 
   @Override
   public String finish() {
       return "You can use normal MacOs PC";
   }
}

抽象工厂类

public abstract class Factory {
   abstract <T extends InstallSoftware> T getComputer(Class<T> clz);
}

工厂类的具体实现

public class ComputerFactory extends Factory {

   /**
    * 利用反射方式生产具体的产品对象
    *
    * @param clz
    * @param <T>
    * @return
    */
   public <T extends InstallSoftware> T getComputer(Class<T> clz) {
       InstallSoftware installSoftware = null;
       try {
           installSoftware = (InstallSoftware) Class.forName(clz.getName()).newInstance();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       }
       return (T) installSoftware;
   }
}

场景类

public class Client {
   public static void main(String[] args) {
       //现在生产一批windows系统的电脑
       //这里举例子说明,具体驱动就不一一列举了.
       ComputerFactory computerFactory = new ComputerFactory();
       WindowsSoftware windowsSoftware = computerFactory.getComputer(WindowsSoftware.class);
       windowsSoftware.installSystem("Windows 10");
       windowsSoftware.installAudioDriver("Windows AudioDriver");
       windowsSoftware.installGPUDriver("Windows GPUDriver");
       windowsSoftware.installKeyBoardDriver("Windows KeyBoardDriver");
       windowsSoftware.installMainBoxdriver("Windows MainBoxdrive");
       windowsSoftware.installMouseDriver("Windows MouseDriver");
       windowsSoftware.installNetCardDriver("Windows NetCardDriver");
       windowsSoftware.installUSBDriver("Windows USBDriver");
       System.out.println(windowsSoftware.finish());

       //现在要生产一批Ubuntu系统的电脑
       UbuntuSoftware ubuntuSoftware = computerFactory.getComputer(UbuntuSoftware.class);
       //Ubuntu系统一般只要安装好系统就可以
       ubuntuSoftware.installSystem("Ubuntu 14.04");
       System.out.println(ubuntuSoftware.finish());

       //现在又要生产一批Mac(黑苹果)系统的电脑
       MacSoftware macSoftware = computerFactory.getComputer(MacSoftware.class);
       //黑苹果安装就有点繁琐了,首先要找驱动什么的,替换原有驱动等等,如果对黑苹果有兴趣,可去远景论坛爬贴学习.
       //黑苹果应该是先把找好的驱动什么的融入到安装系统中,然后才开始安装mac系统
       macSoftware.installAudioDriver("Mac AudioDriver");
       macSoftware.installGPUDriver("Mac GPUDriver");
       macSoftware.installKeyBoardDriver("Mac KeyBoardDriver");
       macSoftware.installMainBoxdriver("Mac MainBoxdriver");
       macSoftware.installMouseDriver("Mac MouseDriver");
       macSoftware.installNetCardDriver("Mac NetCardDriver");
       macSoftware.installUSBDriver("Mac USBDriver");
       macSoftware.installSystem("Mac Os 10.11.6");
       System.out.println(macSoftware.finish());
   }
}

工厂方法模式的拓展

​工厂方法模式一般有4种拓展,分别为简单工厂模式、升级为多个工厂类、替换单例模式、延迟初始化。

​下面我们详细说明这4个拓展。

简单工厂模式(静态工厂模式)

如果一个模块仅需要一个工厂类,使用静态方法就可以,所有我们把上诉工厂类的具体实现(ComputerFactory)里的方法改为静态就可以了:

public class ComputerFactory{

   /**
    * 利用反射方式生产具体的产品对象
    *
    * @param clz
    * @param <T>
    * @return
    */
   public static  <T extends InstallSoftware> T getComputer(Class<T> clz) {
       InstallSoftware installSoftware = null;
       try {
           installSoftware = (InstallSoftware) Class.forName(clz.getName()).newInstance();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       }
       return (T) installSoftware;
   }
}

说明

上诉代码与原有相比,有2处变化,一处是去掉了Factory抽象类,一处是把getComputer设为静态方法。

场景类也要做一些适当变化:

public class Client {
       public static void main(String[] args) {
           //现在生产一批windows系统的电脑
           //这里举例子说明,具体驱动就不一一列举了.
          
           WindowsSoftware windowsSoftware = ComputerFactory.getComputer(WindowsSoftware.class);
           windowsSoftware.installSystem("Windows 10");
           windowsSoftware.installAudioDriver("Windows AudioDriver");
           windowsSoftware.installGPUDriver("Windows GPUDriver");
           windowsSoftware.installKeyBoardDriver("Windows KeyBoardDriver");
           windowsSoftware.installMainBoxdriver("Windows MainBoxdrive");
           windowsSoftware.installMouseDriver("Windows MouseDriver");
           windowsSoftware.installNetCardDriver("Windows NetCardDriver");
           windowsSoftware.installUSBDriver("Windows USBDriver");
           System.out.println(windowsSoftware.finish());

           //现在要生产一批Ubuntu系统的电脑
           UbuntuSoftware ubuntuSoftware = ComputerFactory.getComputer(UbuntuSoftware.class);
           //Ubuntu系统一般只要安装好系统就可以
           ubuntuSoftware.installSystem("Ubuntu 14.04");
           System.out.println(ubuntuSoftware.finish());

           //现在又要生产一批Mac(黑苹果)系统的电脑
           MacSoftware macSoftware = ComputerFactory.getComputer(MacSoftware.class);
           //黑苹果安装就有点繁琐了,首先要找驱动什么的,替换原有驱动等等,如果对黑苹果有兴趣,可去远景论坛爬贴学习.
           //黑苹果应该是先把找好的驱动什么的融入到安装系统中,然后才开始安装mac系统
           macSoftware.installAudioDriver("Mac AudioDriver");
           macSoftware.installGPUDriver("Mac GPUDriver");
           macSoftware.installKeyBoardDriver("Mac KeyBoardDriver");
           macSoftware.installMainBoxdriver("Mac MainBoxdriver");
           macSoftware.installMouseDriver("Mac MouseDriver");
           macSoftware.installNetCardDriver("Mac NetCardDriver");
           macSoftware.installUSBDriver("Mac USBDriver");
           macSoftware.installSystem("Mac Os 10.11.6");
           System.out.println(macSoftware.finish());
         
              }
}

说明

运行结果没有发生变化,而且调用者比较简单,缺点是工厂类的拓展比较困难,不符合开闭原则,但是它仍然是一个非常实用的设计模式。

升级为多个工厂类

我们在做一个比较复杂的项目的时候,所有产品类都放到一个工厂方法中进行初始化会使代码结构不清晰。例如:一个产品类有5个具体实现。每个实现类的初始化(不仅仅是new,初始化包括new一个对象,并对对象设置一定的初始值)方法都不相同,如果写在一个工厂方法中,势必会导致该方法巨大无比,这时我们把工厂方法模式升级为多个工厂类就可以解决这个问题,下面我们还是以上述案例为例,但是每个电脑安装的驱动版本不同。下面是修改后的简单的UML类图(方法全部省略了)


我们以生产不同软件/系统版本的windows电脑为例,其他2种实现方式一致。

其实就是在文章最开始的例子中把一个工厂类分成了3个工厂类,使其各个工厂各司其职,提升工厂产量。

Factory修改为

public abstract class Factory {
   public abstract  InstallSoftware getComputer();
}

增加WindowsFactory

public class WindowsFactory extends Factory {

   @Override
  public InstallSoftware getComputer() {
       return new WindowsSoftware();
   }
}

场景类

public class Client2 {

   public static void main(String[] args) {
       //这里举例子说明,具体驱动就不一一列举了.
       WindowsFactory windowsFactory = new WindowsFactory();
       WindowsSoftware windowsSoftware = (WindowsSoftware) windowsFactory.getComputer();
       windowsSoftware.installSystem("Windows 10");
       windowsSoftware.installAudioDriver("Windows AudioDriver");
       windowsSoftware.installGPUDriver("Windows GPUDriver");
       windowsSoftware.installKeyBoardDriver("Windows KeyBoardDriver");
       windowsSoftware.installMainBoxdriver("Windows MainBoxdrive");
       windowsSoftware.installMouseDriver("Windows MouseDriver");
       windowsSoftware.installNetCardDriver("Windows NetCardDriver");
       windowsSoftware.installUSBDriver("Windows USBDriver");
       System.out.println(windowsSoftware.finish());
   }
}

说明:其他2种就不举例了,实现方式类似。实现各自的Factory使其继承自Factory抽象类。

替换单例模式

通过反射实现,下面举个例子:

单例类

public class Singleton {
    //不允许通过new产生一个对象
   private Singleton() {
   }
   
   public void doSomething() {
        //业务处理       
    }
}

负责生成单例的工厂类

public class SingletonFactory {
       private static Singleton singleton;
       static {
           try {
               Class c1=Class.forName(Singleton.class.getName());
               //获得无参构造
               Constructor constructor=c1.getDeclaredConstructor();
               //设置无参构造是可以访问的
               constructor.setAccessible(true);
               //产生一个实体对象
               singleton=(Singleton)constructor.newInstance();
           } catch (ClassNotFoundException e) {
               e.printStackTrace();
           } catch (NoSuchMethodException e) {
               e.printStackTrace();
           } catch (IllegalAccessException e) {
               e.printStackTrace();
           } catch (InstantiationException e) {
               e.printStackTrace();
           } catch (InvocationTargetException e) {
               e.printStackTrace();
           }
       }

       public static Singleton getSingleton() {
           return singleton;
       }
}

延迟初始化

何为延迟初始化(Lazy initialization)?一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。延迟初始化是工厂方法模式的一个扩展应用,其通用类图如图如下所示。


ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留,Product和ConcreteProduct是一个示例代码。ProductFactory如代码清单如下所示

public class ProductFactory {
        private static final Map<String, Product> prMap = new HashMap();

        public static synchronized Product createProduct(String type)
                throws Exception {
            Product product = null;
            // 如果Map中已经有这个对象
            if (prMap.containsKey(type)) {
                product = prMap.get(type);
            } else {
                if (type.equals("Product1")) {
                    product = new ConcreteProduct1();
                } else {
                    product = new ConcreteProduct2();
                }
                // 同时把对象放到缓存容器中
                prMap.put(type, product);
            }
            return product;
        }
    }

通过定义一个Map容器,容纳所有产生的对象,如果在Map容器中已经有的对象,则直接取出返回;如果没有,则根据需要的类型产生一个对象并放入到Map容器中,以方便下次调用。

延迟加载框架是可以扩展的,例如限制某一个产品类的最大实例化数量,可以通过判断Map中已有的对象数量来实现,这样的处理是非常有意义的,例如JDBC连接数据库,都会 要求设置一个MaxConnections最大连接数量,该数量就是内存中最大实例化的数量。 延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,涉及多方面的交互,则可以通过延迟加载降低对象的产生和销毁带来的复杂性。