zoukankan      html  css  js  c++  java
  • 工厂模式 详解

    工厂模式严格意义来说是三种模式(简单工厂模式,工厂方法模式,抽象工厂模式)

    简单工厂模式

      本质

      选择实现

      功能:工厂嘛,就是用来创造东西的。在java里面,通常情况下用来创造接口的,但是也可以创造抽象类,甚至是一个具体的类实例。

      静态工厂

        使用简单工厂的时候,通常不用创建简单工厂类的类实例,没有创建实例的必要。因此可以把简单工厂类实现成一个工具类,直接使用静态方法就可以了。也就是说简单工厂方法通常是静态的,所有也被称为静态工厂。如果要防止客户端无谓的创造简单工厂实例,还可以把简单工厂的构造方法私有化。

      万能工厂

        一个简单工厂可以包含很多可以用来构造东西的方法,这些方法可以创建不同的接口,抽象类或者是类实例。一个简单工厂理论上是可以构造任何东西,所有又称之为“万能工厂”。

      简单工厂创建实例的范围

        虽然理论上讲,简单工厂什么都可以创建,但对于简单工厂创建实例的范围,通常不要太大,建议控制在一个独立的组件级别或者一个模块级别,也就是一个组件或者模块的简单工厂。否则这个简单工厂的职责不明,有点大杂烩的感觉。

      简单工厂的命名建议

        类名称:模块名称+Factory

        方法名称:“get+接口名称”或者“create+接口名称”。

      简单工厂实现

        简单工厂的方法大多是用来创建接口的,但仔细分析就会发现,真正实现功能的是具体实现类,这些实现类是以及做好的,并不是真的需要简单工厂来创建,简单工厂的方法无外乎就是:实现了选择一个合适的实现类来使用。

        所有简单工厂方法的内部主要实现的功能是:”选择合适的实现类“来创建实例对象。

        要实现选择,那么就需要选择的条件或者选择的参数,条件和参数的来源就有以下几种:  

          来源于客户段,由Client来传入参数

          来源于配置文件,从配置文件获取用于判断的值

          来源于程序运行期的某个值,比如从缓存中获取某个运行期的值

        实现代码

          public class Factory{

            public static Api createApi(int type){

              Api api = null;

              if(type==1){

                api = new Impl();

              }else if(type==2){

                api = new Impl2();

              }else if(type==3){

                api = new Impl3();

              }  

              return api;

            }

          }

        这种方式每次新增一个实现类都要修改工厂类的实现,肯定不是一个好的实现方式。现在想要新增一个不要修改工厂类,需要怎么做?

        一个方案是使用配置文件,当有了新的实现类后只要在配置文件中配置上新的实现类即可。在简单工厂方法里面可以使用反射,也可以使用IOC/DI(控制反转/依赖注入)来实现。

        实现代码

          首先在配置文件中配置实现类,配置文件这里用简单的properties文件,一般开发中用XML文件。定义一个名称为“FactoryTest.properties”的配置文件,放在Factory同一个包下面,

            文件内容:表述实现类名称(String)=实现类类全名(String)

          工厂类实现代码

     

    public class Factory {
      public static Api getApi(String name) {
        Properties p = new Properties();
        InputStream in = null;
        try {
          in = Factory.class.getResourceAsStream("FactoryTest.properties");
          p.load(in);
        } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }finally {
          try {
            in.close();
          } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
        Api api = null;
        try {
          api = (Api)Class.forName(p.getProperty(name)).newInstance();
        } catch (InstantiationException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        } catch (IllegalAccessException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        } catch (ClassNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        return api;
      }

            public static void main(String[] args) {
              Factory.getApi(表述实现类名称).study();
            }

      }

      简单工厂优点

        帮助封装

          简单工厂虽然简单,但非常友好的帮助我们实现了组件的封装,然后让组件外部能真正的面向接口编程。

        解耦

          通过简单工厂,实现了客户端和具体实现类的解耦。

          如上,客户端根本不知道具体是由谁来实现,也不知具体是如何实现的,客户端只是通过工厂获取它需要的接口对象。

    工厂方法模式

      本质

       延迟到子类来选择实现 

      定义

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

      解决思路

        工厂方法模式的解决思路很有意思,那就是不解决,采取无为而治的方式:不是需要接口对象吗,那就定义一个方法来创建;可是事实上它自己是不知道怎么创建这个接口对象的,没关系,定义成抽象方法就可以了,自己实现不了就让子类去实现,这样这个对象本身就可以只是面向接口编程,而无需关心到底如何创建接口对象了。

      实现代码

        //工厂方法所创建的对象的接口

        public interface Api {

          //定义Api的属性和方法

        }

        //具体的Api对象

        public class ApiImp implements Api {

          //实现Api要求的方法

        }

        //创建器,声明工厂方法

        public abstract class Creator{

          protected abstract Api factoryMethod();

          public void someOperation(){

            Api api = factoryMethod();

          }

        }

        //具体创建器实现对象

        public class ConcreteCreator extends Creator {

          protected abstract Api factoryMethod(){

            return new ApiImpl();

          }

        }

    抽象工厂模式

      本质

        选择产品簇的实现

      定义

        提供一个创建一系列相关或相互依赖的接口,而无需指定它们具体的类。

      用抽象工厂模式解决问题的思路

        两个问题点:一个是只知道所需要的一系列对象的接口,而不知具体实现,或者不知道具体使用哪一个实现;另外一个是这一系列对象是相关或相互依赖的,也就是说既要创建接口的对象,还有约束它们之间的关系。

      注意:简单工厂模式和方法工厂模式关注的是单个产品对象的创建。

      事例代码:

    /**
    * 抽象工厂接口,声明创建抽象产品对象的操作
    * @author Administrator
    *
    */
    public interface AbstractFactory {
      /**
      * 创建抽象产品A对象
      */
      public AbstractProductA CreateProductA();
      /**
      * 创建抽象产品B对象
      */
      public AbstractProductB CreateProductB();
    }

      省略产品A和产品B的具体实现代码

        /**

        * 具体的工厂实现对象,实现创建具体的产品对象的操作

        **/

        public class ConcreteFactory1 implements AbstractFactory {

          public AbstractProductA createProductA(){

            return new ProductA1();

          }

          public AbstractProductB createProductB(){

            return new ProductB1();

          }

        }

        /**

        * 具体的工厂实现对象,实现创建具体的产品对象的操作

        **/

        public class ConcreteFactory2 implements AbstractFactory {

          public AbstractProductA createProductA(){

            return new ProductA2();

          }

          public AbstractProductB createProductB(){

            return new ProductB2();

          }

        }

        客户端:

        public class Client{

          public static void main(String[] agrs){

            //创建抽象工厂对象

            AbstractFactory af = new ConcreteFactory1();

            //通过工厂来获取一系列的对象,如产品A和产品B

            af.createProductA();

            af.createProductB();

          }

        }

      实现抽象工厂模式的灵活性,可以和简单工厂模式组合(具体的创建对象用简单工厂模式来做)。

  • 相关阅读:
    利用ffmpeg获取视频帧
    GCN(Graph Convolutional Network)的简单公式推导
    在导入pytorch时libmkl_intel_lp64.so找不到
    pytorch学习: 构建网络模型的几种方法
    pytorch: 准备、训练和测试自己的图片数据
    pytorch学习:准备自己的图片数据
    Pytorch入门实例:mnist分类训练
    tensorflow 1.0 学习:十图详解tensorflow数据读取机制
    概率分布之间的距离度量以及python实现(四)
    Attribute application@allowBackup value=(true) from AndroidManifest.xml:7:9-35
  • 原文地址:https://www.cnblogs.com/johnnyC/p/9451079.html
Copyright © 2011-2022 走看看