【创造型格局二】工厂方法(Factory Method)

2.2 格局结构和表达##

工厂方法格局的结构如图所示:

工厂方法情势的布局

Product:定义工厂方法所创建的目标的接口,也就是实在需要运用的对象的接口。

ConcreteProduct:具体的Product接口的实现目的。

Creator:创设器,申明工厂方法,工厂方法一般会回来一个Product类型的实例对象,而且多是纸上谈兵方法。也得以在Creator里面提供工厂方法的默认实现,让工厂方法重临一个缺省的Product类型的实例对象。

ConcreteCreator:具体的成立器对象,覆盖实现Creator定义的厂子方法,重临具体的Product实例。

3.5 工厂方法格局的利弊##

  1. 可以在不知具体贯彻的图景下编程

工厂方法情势能够让你在实现效益的时候,就算急需某个产品对象,只需要选用产品的接口即可,而无需关注具体的实现。采纳具体贯彻的天职延迟到子类去完成。

更易于扩大对象的新本子。

工厂方法给子类提供了一个关系,使得扩充新的目标版本变得万分容易。譬如说下面示例的参数化工厂方法实现中,扩充一个新的导出Xml文件格式的落实,已有的代码都不会转移,只要新出席一个子类来提供新的厂子方法实现,然后在客户端应用那么些新的子类即可。

除此以外这里涉及的牵连,就是我们经常说的钩子方法(hook),这些会在背后讲模板方法格局的时候详细点表明。

  1. 一连平行的类层次

工厂方法除了创设产品对象外,在连续平行的类层次上也大显身手。这一个在眼前已经详细讲述了。

  1. 具体产品对象和工厂方法的耦合性

在工厂方法格局里面,工厂方法是急需创建产品对象的,也就是内需拔取具体的产品对象,并创建它们的实例,由此具体产品对象和工厂方法是耦合的。

2 解决方案#

3 格局教学#

2.3 工厂方法模式示例代码##

  1. 先看看Product的概念,示例代码如下:

/**
   * 工厂方法所创建的对象的接口
   */
public interface Product {
      //可以定义Product的属性和方法
}
  1. 再看看具体的Product的兑现目的,示例代码如下:

/**
   * 具体的Product对象
   */
public class ConcreteProduct implements Product {
      // 实现Product要求的方法
}
  1. 接下去看看创设器的概念,示例代码如下:

/**
   * 创建器,声明工厂方法
   */
public abstract class Creator {
      /**
       * 创建Product的工厂方法
       * @return Product对象
       */
      protected abstract Product factoryMethod();
      /**
       * 示意方法,实现某些功能的方法
       */
      public void someOperation() {
          //通常在这些方法实现中,需要调用工厂方法来获取Product对象
          Product product = factoryMethod();
      }
}
  1. 再看看具体的创造器实现目标,示例代码如下:

/**
   * 具体的创建器实现对象
   */
public class ConcreteCreator extends Creator {
      protected Product factoryMethod() {
          //重定义工厂方法,返回一个具体的Product对象
          return new ConcreteProduct();
      }
}

3.1 认识工厂方法形式##

  1. 情势的功能

工厂方法的重中之重职能是让父类在不精通具体贯彻的事态下,完成自身的功能调用,而具体的实现延迟到子类来贯彻。

如此在设计的时候,不用去考虑具体的兑现,需要某个对象,把它通过工厂方法重回就好了,在选取这多少个目标实现效益的时候如故通过接口来操作,这卓殊类似于IoC/DI的思考,那多少个在末端给我们稍详细点介绍一下。

  1. 兑现成抽象类

厂子方法的兑现中,经常父类会是一个抽象类,里面包含创造所需对象的虚幻方法,那么些抽象方法就是工厂方法。

此间要专注一个题目,子类在贯彻这么些抽象方法的时候,通常并不是的确由子类来落实具体的成效,而是在子类的措施里面做取舍,采取具体的产品实现目的

父类里面,一般说来会有使用这些产品对象来促成自然的效果的办法,而且那么些方法所实现的意义通常都是集体的意义,不管子类拔取了何种具体的成品实现,这么些主意的机能总是能科学履行。

  1. 落实成现实的类

理所当然也得以把父类实现成为一个切实的类,这种情景下,万般是在父类中提供获取所需对象的默认实现模式,这样尽管没有现实的子类,也可以运转

平常这种场所仍旧需要切实的子类来支配具体要哪些创制父类所急需的目的。也把这种情形称为工厂方法为子类提供了联系,通过工厂方法,可以让子类对象来掩盖父类的贯彻,从而提供更好的灵活性。

  1. 厂子方法的参数和重返

厂子方法的实现中,可能需要参数,以便控制到底拔取哪种具体的贯彻。也就是说经过在空虚方法里面传递参数,在子类实现的时候依照参数举办精选,看看到底应当创立哪一个现实的贯彻目标

相似厂子方法重返的是被成立对象的接口对象,当然也足以是抽象类或者一个实际的类的实例。

  1. 什么人来使用工厂方法创立的靶子

此处首先要搞精通一件事情,就是什么人在采纳工厂方法创立的对象?

骨子里,在工厂方法模式里面,应该是Creator中的另外措施在使用工厂方法创立的靶子,尽管也足以把工厂方法创造的对象直接提供给Creator外部使用,但工厂方法格局的本意,是由Creator对象内部的艺术来选取工厂方法创造的靶子,也就是说,工厂方法一般不提供给Creator外部使用

客户端应该是使用Creator对象,或者是应用由Creator创立出来的目标。对此客户端应用Creator对象,这一个时候工厂方法创立的对象,是Creator中的某些方法运用。对于利用那个由Creator成立出来的目的,这一个时候工厂方法创制的靶子,是整合客户端需要的对象的一部分。分别举例来证实。

① 客户端拔取Creator对象的情状

例如后面的演示,对于“实现导出数据的事务效用对象”的类ExportOperate,它有一个export的不二法门,在这一个措施里面,需要动用具体的“导出的文本对象的接口对象”
ExportFileApi,而ExportOperate是不知道具体的ExportFileApi实现的,那么如何做的吗?就是概念了一个工厂方法,用来重临ExportFileApi的靶子,然后export方法会采纳这些工厂方法来收获它所急需的目的,然后实施效果

本条时候的客户端是肿么办的吗?这多少个时候客户端首要就是选拔这多少个ExportOperate的实例来成功它想要完成的法力,也就是客户端应用Creator对象的情状,简单描述这种境况下的代码结构如下:

/**
   * 客户端使用Creator对象的情况下,Creator的基本实现结构
   */
public abstract class Creator {
      /**
       * 工厂方法,一般不对外
       * @return 创建的产品对象
       */
      protected abstract Product factoryMethod();
      /**
       * 提供给外部使用的方法,
       * 客户端一般使用Creator提供的这些方法来完成所需要的功能
       */
      public void someOperation(){
          //在这里使用工厂方法
          Product p = factoryMethod();
      }
}

② 客户端应用由Creator创造出来的目的

除此以外一种是由Creator向客户端重返由“工厂方法创立的对象”来构建的对象,那个时候工厂方法创立的目的,是组成客户端需要的靶子的一片段。简单描述这种场所下的代码结构如下:

/**
   * 客户端使用Creator来创建客户端需要的对象的情况下,Creator的基本实现结构
   */
public abstract class Creator {
      /**
       * 工厂方法,一般不对外,创建一个部件对象
       * @return 创建的产品对象,一般是另一个产品对象的部件
       */
      protected abstract Product1 factoryMethod1();
      /**
       * 工厂方法,一般不对外,创建一个部件对象
       * @return 创建的产品对象,一般是另一个产品对象的部件
       */
      protected abstract Product2 factoryMethod2();
      /**
       * 创建客户端需要的对象,客户端主要使用产品对象来完成所需要的功能
       * @return 客户端需要的对象
       */
      public Product createProduct(){
          //在这里使用工厂方法,得到客户端所需对象的部件对象
          Product1 p1 = factoryMethod1();
          Product2 p2 = factoryMethod2();
          //工厂方法创建的对象是创建客户端对象所需要的
          Product p = new ConcreteProduct();
          p.setProduct1(p1);
          p.setProduct2(p2);

          return p;
      }
}

小结一下:在工厂方法情势里面,客户端仍然拔取Creator对象,要么使用Creator创制的对象,一般客户端不直接利用工厂方法。当然也足以直接把工厂方法显露给客户端操作,然而一般不这么做。

  1. 厂子方法形式的调用顺序示意图

是因为客户端应用Creator对象有二种典型的境况,由此调用的一一示意图也分做二种意况,先看看客户端应用由Creator成立出来的对象意况的调用顺序示意图,如图所示:

客户端接纳由Creator成立出来的目的情况的调用顺序示意图

接下去看看客户端采纳Creator对象时候的调用顺序示意图,如图所示:

客户端应用Creator对象时候的调用顺序示意图

1.3 有何问题##

解析上边要实现的采纳框架,不管用户挑选怎么的导出格式,最终导出的都是一个文本,而且系统并不知道究竟要导出成为咋样的文本,因而应该有一个联结的接口,来讲述系统最后生成的靶子,并操作输出的文件。

先把导出的公文对象的接口定义出来,示例代码如下:

/**
 * 导出的文件对象的接口
 */
public interface ExportFileApi {
   /**
    * 导出内容成为文件
    * @param data 示意:需要保存的数据
    * @return 是否导出成功
    */
   public boolean export(String data);
}

对此实现导出数据的政工功用对象,它应当按照需要来创立相应的ExportFileApi的兑现目标,因为特定的ExportFileApi的贯彻是与具体的作业有关的。可是对于贯彻导出数据的事务效率对象而言,它并不知道应该创造哪一个ExportFileApi的兑现目的,也不明了哪些创立。

也就是说:对于落实导出数据的业务职能对象,它需要创立ExportFileApi的求实实例对象,可是它只知道ExportFileApi接口,而不领会其现实的兑现。这该怎么做吧?

1.1 导出多少的施用框架##

考虑这么一个实在运用:落实一个导出数据的使用框架,来让客户采用数据的导出情势,并确实执行多少导出

在部分事实上的公司应用中,一个商厦的序列往往分散在广大个例外的地点运作,比如各样分店或者是门市点,集团从未建立全公司专网的实力,但是又不甘于让事情数据实时的在广域网上传递,一个是考虑数据安全的题材,一个是运作速度的题目。

这种系统平时会有一个折中的方案,这就是各种子公司内运行体系的时候是独立的,是在和谐分公司的局域网内运行。然后在每一日工作终止的时候,各类子公司会导出自己的事务数据,然后把事情数据打包通过网络传送给母集团,或是专人把多少送到总公司,然后由母公司举行数据导入和核算。

平凡这种系统,在导出数据上,会有一些预定的办法,比如导出成:文本格式、数据库备份模式、Excel格式、Xml格式等等

现在就来考虑实现如此一个应用框架。在继续此前,先来打听部分有关框架的学问。

1.2 框架的基础知识##

  1. 框架是怎么着

简言之点说:框架就是能成功一定效能的毛坯软件。

就其本质而言,框架是一个软件,而且是一个半出品的软件。所谓半成品,就是还不可以一心落实用户需要的功效,框架只是实现用户需要的成效的一部分,还索要更加加工,才能成为一个满足用户需要的、完整的软件。由此框架级的软件,它的基本点客户是开发人员,而不是最后用户。

些微朋友会想,既然框架只是个半成品,这何必要去读书和行使框架呢?学习成本也不算小,这就是因为框架能成功一定的机能,也就是这“框架已经成功的必然的功效”在诱惑着开发人员,让我们投入去上学和利用框架。

  1. 框架能干什么

能不负众望一定意义,加快利用开发进度。

出于框架形成了迟早的效应,而且平时是有的基础的、有难度的、通用的意义,那就避免大家在行使开发的时候完全从头伊始,而是在框架已有的效益之上继续支付,也就是说会复用框架的效率,从而加快利用的开发进度。

给咱们一个完好无损的顺序架构。框架定义了利用的完好布局,包括类和目的的划分,各部分的基本点责任,类和目的怎么协作,以及控制流程等等。

近年来Java界大多数风行的框架,大都出自大师真迹,设计都很可观。基于这样的框架来支付,一般会依据框架已经规划好的构造来进展支付,从而让我们付出的应用程序的布局也针锋相对变得美好了。

  1. 对框架的掌握

据悉框架来支付,事情或者这些事情,只是看何人做的题目。

对此应用程序和框架的涉嫌,可以用一个图来概括描述一下,如图所示:

应用程序和框架的关系

尽管没有框架,那么客户要求的有着机能都由开发人士自己来支付,没问题,同样可以兑现用户要求的功用,只是开发人士的工作多点。

一经有了框架,框架本身完成了必然的效果,那么框架已部分职能,开发人士就可以不做了,开发人士只需要形成框架没有的效益,最后同样是做到客户要求的有着机能,可是开发人员的办事就收缩了。

也就是说,遵照框架来支付,软件要形成的功能并从未变动,还是客户要求的具备效能,也就是“事情依然那一个事情”的情趣。但是有了框架过后,框架形成了一有些机能,然后开发人士再形成部分效益,最终由框架和开发人士合起来完成了上上下下软件的效应,也就是看那个效能“由何人做”的题材

据悉框架开发,可以不去做框架所做的事体,但是相应明白框架在干什么,以及框架是何等实现相应效率的。

事实上,在事实上支出中,应用程序和框架的涉及,经常都不会如下边讲述的那么,分得那么精通,更为广泛的是互为交互的,也就是应用程序做一些干活,然后框架做一些工作,然后应用程序再做一些做事,然后框架再做一些做事,如此交错,最终由应用程序和框架组合起来完成用户的机能要求。

也用个图来证实,如图所示:

应用程序和框架组合起来

比方把这些由应用程序和框架组合在一起组成的矩形,当作最后形成的软件。试想一下,假若您不懂框架在干什么的话,相当于框架对你来讲是个黑盒,也就是相当于在下面图中,去掉框架的两块,会意识什么样?没错,剩下的应用程序是支离破碎的,是相互分隔开来的。

这会导致一个非凡沉重的问题,整个应用是何等运转起来的,你是不了解的,也就是说对您而言,项目已经失控了,从项目管理的角度来讲,这是很危险的。

因此,在基于框架开发的时候,即使我们得以不去做框架所做的事务,但是应当搞了然框架在干什么,假诺条件许可的话,还相应搞了解框架是怎样落实相应效用的,至少应当把大约的落实思路和实现步骤搞精晓,这样大家才能完全的掌控一切项目,才能尽量裁减出现项目失控的情况

  1. 框架和设计模式的关联

设计情势比框架更抽象。

框架已经是促成出来的软件了,即使只是个半成品的软件,但归根结底是已经实现出来的了。而设计情势的本位还在于解决问题的方案上,也就是还栖息在盘算的层面。由此设计格局比框架进一步抽象

设计形式是比框架更小的系统布局元素。

总的来说,框架是现已落实出来的软件,并落实了一名目繁多的效应,因而一个框架,平日会含有六个设计格局的施用。

框架比设计形式更加特例化。

框架是完结一定意义的毛坯软件,也就是说,框架的目的很明朗,就是要解决某一个领域的某些问题,那是很现实的职能,不同的小圈子落实出来的框架是不同等的。

而设计情势还停留在构思的局面,在不同的小圈子都足以接纳,只要相应的题材切合用某个设计格局来解决。由此框架连接针对一定领域的,而设计情势更加强调从思想上,从艺术上来解决问题,更加通用化。

3.4 参数化工厂方法##

所谓参数化工厂方法指的就是:通过给工厂方法传递参数,让工厂方法遵照参数的不同来创制不同的制品对象,这种景观就被称之为参数化工厂方法。本来工厂方法创设的不同的制品必须是同一个Product类型的。

来改造前边的示范,现在有一个厂子方法来创建ExportFileApi这个产品的目标,但是ExportFileApi接口的切实可行落实无数,为了有利于创制的挑选,直接从客户端传入一个参数,这样在急需成立ExportFileApi对象的时候,就把那么些参数传递给工厂方法,让工厂方法来实例化具体的ExportFileApi实现目的。

  1. 先来看Product的接口,就是ExportFileApi接口,跟前边的示范没有此外变化,为了方便我们查看,这里再度一下,示例代码如下:

/**
   * 导出的文件对象的接口
   */
public interface ExportFileApi {
      /**
       * 导出内容成为文件
       * @param data 示意:需要保存的数据
       * @return 是否导出成功
       */
      public boolean export(String data); 
}
  1. 同样提供保存成文本文件和保留成数据库备份文件的落实,跟前面的演示没有其他变动,示例代码如下:

public class ExportTxtFile implements ExportFileApi{
      public boolean export(String data) {
          //简单示意一下,这里需要操作文件
          System.out.println("导出数据"+data+"到文本文件");
          return true;
      }
}
public class ExportDB implements ExportFileApi{
      public boolean export(String data) {
          //简单示意一下,这里需要操作数据库和文件
          System.out.println("导出数据"+data+"到数据库备份文件");
          return true;
      }
}
  1. 接下去该看看ExportOperate类了,那个类的变通大致如下:

ExportOperate类中的创设产品的厂子方法,平常需要提供默认的实现,不空洞了,也就是成为正规形式。

ExportOperate类也不再定义成抽象类了,因为有了默认的实现,客户端可能需要一贯动用那个目的。

设置一个导出类型的参数,通过export方法从客户端传入。

/**
   * 实现导出数据的业务功能对象
   */
public class ExportOperate {
      /**
       * 导出文件
       * @param type 用户选择的导出类型
       * @param data 需要保存的数据
       * @return 是否成功导出文件
       */
      public boolean export(int type,String data){
          //使用工厂方法
          ExportFileApi api = factoryMethod(type);
          return api.export(data);
      }
      /**
       * 工厂方法,创建导出的文件对象的接口对象
       * @param type 用户选择的导出类型
       * @return 导出的文件对象的接口对象
       */
      protected ExportFileApi factoryMethod(int type){
          ExportFileApi api = null;
          //根据类型来选择究竟要创建哪一种导出文件对象
          if(type==1){
              api = new ExportTxtFile();
          }else if(type==2){
              api = new ExportDB();
          }
          return api;
      }
}
  1. 这儿的客户端,相当简单,直接使用ExportOperate类,示例代码如下:

public class Client {
      public static void main(String[] args) {
          //创建需要使用的Creator对象
          ExportOperate operate = new ExportOperate();
          //调用输出数据的功能方法,传入选择到处类型的参数
          operate.export(1,"测试数据");
      }
}

测试看看,然后修改一下客户端的参数,体会一下透过参数来摘取具体的导出实现的历程。这是一种很宽泛的参数化工厂方法的实现形式,但是也仍然有把参数化工厂方法实现成为虚幻的,这点要注意,并不是说参数化工厂方法就无法促成成为抽象类了。只是一般情形下,参数化工厂方法,在父类都会提供默认的贯彻

  1. 扩展新的实现

利用参数化工厂方法,扩张起来会分外容易,已有的代码都不会改变,只要新参预一个子类来提供新的工厂方法实现,然后在客户端应用那一个新的子类即可。

这种实现模式还有一个有趣的功能,就是子类可以采纳性覆盖,不想覆盖的职能还可以够再次回到去让父类来落实,很有意思。

先扩展一个导出成xml文件的实现,试试看,示例代码如下:

/**
   * 导出成xml文件的对象
   */
public class ExportXml implements ExportFileApi{
      public boolean export(String data) {
          //简单示意一下
          System.out.println("导出数据"+data+"到XML文件");
          return true;
      }
}

接下来扩张ExportOperate类,来投入新的贯彻,示例代码如下:

/**
   * 扩展ExportOperate对象,加入可以导出XML文件
   */
public class ExportOperate2 extends ExportOperate{
      /**
       * 覆盖父类的工厂方法,创建导出的文件对象的接口对象
       * @param type 用户选择的导出类型
       * @return 导出的文件对象的接口对象
       */
      protected ExportFileApi factoryMethod(int type){
          ExportFileApi api = null;
          //可以全部覆盖,也可以选择自己感兴趣的覆盖,
          //这里只想添加自己新的实现,其它的不管
          if(type==3){
              api = new ExportXml();
          }else{
              //其它的还是让父类来实现
              api = super.factoryMethod(type);
          }
          return api;
      }
}

探望此时的客户端,也非常简单,只是在转换传入的参数,示例代码如下:

public class Client {
    public static void main(String[] args) {
        //创建需要使用的Creator对象
        ExportOperate operate = new ExportOperate2();
        //下面变换传入的参数来测试参数化工厂方法
        operate.export(1,"Test1");
        operate.export(2,"Test2");
        operate.export(3,"Test3");
    }
}

3.2 工厂方法模式与IoC/DI##

IoC——Inversion of Control 控制反转

DI——Dependency Injection 看重注入

  1. 怎么着明白IoC/DI

要想知道地点三个概念,就非得搞精通如下的问题:

出席者都有什么人?

倚重:何人依赖于何人?为何需要依靠?

流入:何人注入于什么人?到底注入什么?

操纵反转:何人说了算谁?控制什么?为啥叫反转(有反转就应当有正转了)?

凭借注入和操纵反转是均等概念呢?

(1) 参与者都有什么人:

诚如有三方参与者,一个是某个对象;一个是IoC/DI的器皿;另一个是某个对象的外部资源。

又要名词解释一下,某个对象指的就是即兴的、普通的Java对象;
IoC/DI的容器简单点说就是指用来贯彻IoC/DI效用的一个框架程序;对象的外表资源指的就是目的急需的,可是是从对象外部拿到的,都统称资源
,比如:对象急需的任何对象、或者是目的需要的文本资源等等。

(2) 何人依赖于何人:当然是某个对象看重于IoC/DI的器皿

(3) 为何需要依靠:对象需要IoC/DI的器皿来提供对象急需的外表资源

(4) 什么人注入于何人:很醒目是IoC/DI的器皿 注入 某个对象

(5) 到底注入什么:就是流入某个对象所急需的外部资源

(6) 何人说了算什么人:当然是IoC/DI的器皿来决定目标了

(7) 控制什么:紧假设控制目的实例的创导

(8) 为啥叫反转:

反转是对峙于正向而言的,那么什么样算是正向的吗?考虑一下常规状态下的应用程序,假若要在A里面使用C,你会咋做吧?当然是直接去创制C的靶子,也就是说,是在A类中再接再厉去取得所需要的表面资源C,这种气象被称为正向的。那么咋样是反向呢?就是A类不再主动去赢得C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的流入到A类中。

用图例来表达一下,先看没有IoC/DI的时候,常规的A类应用C类的示意图,如图所示:

常规的A类应用C类的示意图

当有了IoC/DI的容器后,A类不再主动去创制C了,如图所示:

A类不再主动去创造C

而是消极等待,等待IoC/DI的容器获取一个C的实例,然后反向的流入到A类中,如图所示:

伺机IoC/DI的器皿获取一个C的实例

(9) 倚重注入和操纵反转是一致概念呢?

据悉地点的描述,应该能看出来,借助注入和决定反转是对同样件业务的不比描述,从某个地点讲,就是它们描述的角度不同。借助注入是从应用程序的角度在讲述,可以把依赖注入描述完整点:应用程序看重容器成立并流入它所急需的外部资源;而控制反转是从容器的角度在讲述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所急需的外表资源。

(10) 小结一下:

实则IoC/DI对编程带来的最大改变不是从代码上,而是从思想上,暴发了“主从换位”的变通。应用程序原本是不行,要博取什么资源都是主动出击,然而在IoC/DI思想中,应用程序就改成被动的了,被动的等候IoC/DI容器来创设并流入它所急需的资源了。

这般小小的一个变更其实是编程思想的一个大提升,如此这般就使得的分手了对象和它所急需的外部资源,使得它们松散耦合,有利于功效复用,更重要的是驱动程序的全部序列布局变得十分灵活

  1. 厂子方法形式和IoC/DI有什么样关联吗?

从某个角度讲,它们的思考很相近。

地点讲了,有了IoC/DI过后,应用程序就不再主动了,而是消极等待由容器来注入资源,那么在编写代码的时候,一旦要用到表面资源,就会开一个窗口,让容器能注入进来,也就是提供给容器使用的流入的门道,当然这不是我们的最紧要,就不去细细讲了,用setter注入来演示一下,看看使用IoC/DI的代码是怎么体统,示例代码如下:

public class A {
      /**
       * 等待被注入进来
       */
      private C c = null;
      /**
       * 注入资源C的方法
       * @param c 被注入的资源
       */
      public void setC(C c){
          this.c = c;
      }
      public void t1(){
          //这里需要使用C,可是又不让主动去创建C了,怎么办?
          //反正就要求从外部注入,这样更省心,
          //自己不用管怎么获取C,直接使用就好了
          c.tc();
      }
}

接口C的言传身教代码如下:

public interface C {
      public void tc();
}

从下面的以身作则代码可以看来,现在在A里面写代码的时候,凡是碰着了亟需外部资源,那么就提供注入的不二法门,要求从表面注入,自己只管使用那些目的。

public abstract class A1 {
      /**
       * 工厂方法,创建C1,类似于从子类注入进来的途径
       * @return C1的对象实例
       */
      protected abstract C1 createC1();
      public void t1(){
          //这里需要使用C1类,可是不知道究竟是用哪一个
          //也就不主动去创建C1了,怎么办?
          //反正会在子类里面实现,这里不用管怎么获取C1,直接使用就好了
          createC1().tc();
      }
}

子类的言传身教代码如下:

public class A2 extends A1 {
    protected C1 createC1() {
        //真正的选择具体实现,并创建对象
        return new C2();
    }
}

C1接口和前边C接口是千篇一律的,C2这多少个实现类也是空的,只是演示一下,因而就不去体现它们的代码了。

周详回味下面的言传身教,相比它们的贯彻,进一步是从思想层面上,会意识工厂方法形式和IoC/DI的沉思是相似的,都是“主动变被动”,举办了“主从换位”,从而获取了更灵活的程序结构

3.7 相关格局##

  1. 厂子方法形式和架空工厂形式

这两个格局可以结合使用,具体的放到抽象工厂形式中去讲。

  1. 工厂方法格局和模板方法格局

这多少个情势外观类似,都是有一个抽象类,然后由子类来提供部分兑现,然而工厂方法格局的子类专注的是创造产品对象,而模板方法格局的子类专注的是为一定的算法骨架提供一些步骤的贯彻

这六个模式可以结合使用,平日在模板方法形式里面,使用工厂方法来创设模板方法需要的目的。

1 场景问题#

3.3 平行的类层次结构##

  1. 什么是平行的类层次结构呢?

概括点说,要是有五个类层次结构,其中一个类层次中的每个类在另一个类层次中都有一个相应的类的结构,就被称呼平行的类层次结构。

举个例证来说,硬盘对象有许多种,如分成台式机硬盘和台式机硬盘,在台式机硬盘的具体贯彻地方,又有西部数据、西数等不同品牌的落实,同样在记录本硬盘上,也有HGST、日立、IBM等不同品牌的实现;硬盘对象具备温馨的一言一行,如硬盘能储存数据,也能从硬盘上获取数据,不同的硬盘对象对应的作为目的是不均等的,因为不同的硬盘对象,它的行事的兑现情势是不雷同的。尽管把硬盘对象和硬盘对象的所作所为分别描述,那么就结成了如图所示的布局:

平行的类层次结构示意图

硬盘对象是一个类层次,硬盘的所作所为这边也是一个类层次,而且几个类层次中的类是应和的。台式机西捷硬盘对象就对应着硬盘行为里面的台式机西捷硬盘的所作所为;台式机IBM硬盘就对应着台式机IBM硬盘的一言一行,这就是一种典型的平行的类层次结构。

这种平行的类层次结构用来干什么吧?重要用于把一个类层次中的某些行为分离出来,让类层次中的类把原先属于自己的任务,委托给分离出来的类去落实,从而使得类层次本身变得更简便,更易于扩展和复用。

一般来讲,分离出来的这些类的表现,会对应着类层次结构来集团,从而形成一个新的类层次结构,约等于原来对象的一言一行的如此一个类层次结构,而那些层次结构和原来的类层次结构是存在对应关系的,故此被称作平行的类层次结构

  1. 厂子方法格局跟平行的类层次结构有何关联吗?

可以利用工厂方法格局来连续平行的类层次。

看上边的示例图,在各种硬盘对象里面,都有一个厂子方法createHDOperate,通过这多少个工厂方法,客户端就足以得到一个跟硬盘对象相呼应的所作所为目的。在硬盘对象的子类里面,会覆盖父类的工厂方法createHDOperate,以提供跟我相对应的表现目标,从而自然的把五个平行的类层次连接起来使用。

2.4 使用工厂方法形式来兑现示例##

要采用工厂方法格局来贯彻示例,先来遵照工厂方法格局的布局,对应出怎么着是被创设的Product,哪些是Creator。分析要求贯彻的效用,导出的文件对象接口ExportFileApi就一定于是Product,而用来兑现导出数据的作业功能对象就约等于Creator。把Product和Creator分开过后,就足以分别来实现它们了。

选拔工厂方法格局来落实示例的程序结构如图所示:

厂子方法形式来实现示例的程序结构

  1. 导出的文件对象接口ExportFileApi的实现没有变动,这里就不去赘述了

  2. 接下去看看接口ExportFileApi的实现,为了示例简单,只兑现导出文本文件格式和数据库备份文件二种。先看看导出文本文件格式的贯彻,示例代码如下:

/**
   * 导出成文本文件格式的对象
   */
public class ExportTxtFile implements ExportFileApi{
      public boolean export(String data) {
          //简单示意一下,这里需要操作文件
          System.out.println("导出数据"+data+"到文本文件");
          return true;
      }
}

/**
   * 导出成数据库备份文件形式的对象
   */
public class ExportDB implements ExportFileApi{
      public boolean export(String data) {
          //简单示意一下,这里需要操作数据库和文件
          System.out.println("导出数据"+data+"到数据库备份文件");
          return true;
      }
}
  1. Creator这边的兑现,首先看看ExportOperate的兑现,示例代码如下:

/**
   * 实现导出数据的业务功能对象
   */
public abstract class ExportOperate {
      /**
       * 导出文件
       * @param data 需要保存的数据
       * @return 是否成功导出文件
       */
      public boolean export(String data){
          //使用工厂方法
          ExportFileApi api = factoryMethod();
          return api.export(data);
      }
      /**
       * 工厂方法,创建导出的文件对象的接口对象
       * @return 导出的文件对象的接口对象
       */
      protected abstract ExportFileApi factoryMethod();
}
  1. 投入了五个Creator实现,示例代码如下:

/**
   * 具体的创建器实现对象,实现创建导出成文本文件格式的对象
   */
public class ExportTxtFileOperate extends ExportOperate{
      protected ExportFileApi factoryMethod() {
          //创建导出成文本文件格式的对象
          return new ExportTxtFile();
      }
}

/**
   * 具体的创建器实现对象,实现创建导出成数据库备份文件形式的对象
   */
public class ExportDBOperate extends ExportOperate{
      protected ExportFileApi factoryMethod() {
          //创建导出成数据库备份文件形式的对象
          return new ExportDB();
      }
}
  1. 客户端直接创设需要使用的Creator对象,然后调用相应的效劳方法,示例代码如下:

public class Client {
      public static void main(String[] args) {
          //创建需要使用的Creator对象
          ExportOperate operate = new ExportDBOperate();
          //调用输出数据的功能方法
          operate.export("测试数据");
      }
}

3.6 思考工厂方法模式##

  1. 厂子方法模式的本色

厂子方法情势的面目:延迟到子类来选用实现。

密切回味前边的以身作则,你会发觉,工厂方法情势中的工厂方法,在真正落实的时候,一般是先选用具体行使哪一个实际的产品实现目的,然后创制那个现实产品对象的言传身教,然后就可以再次来到去了。也就是说,厂子方法本身并不会去实现产品接口,具体的制品实现是曾经写好了的,工厂方法只要去挑选实现就好了。

稍稍朋友可能会说,这不是跟简单工厂一样吗?

确实从本质上讲,它们是可怜相近的,具体实现上都是在“选拔实现”。唯独也存在不同点,简单工厂是直接在工厂类里面举办“拔取实现”;而工厂方法会把这个工作推迟到子类来兑现,工厂类里面使用工厂方法的地点是借助于肤浅而不是切实可行的贯彻,从而使得系统进一步灵敏,具有更好的可维护性和可增添性。

其实尽管把工厂方法格局中的Creator退化一下,只提供工厂方法,而且那些工厂方法还都提供默认的落实,这不就变成了简要工厂了啊?比如把刚刚示范参数化工厂方法的事例代码拿过来再简化一下,你就能看出来,写得跟简单工厂是基本上的,示例代码如下:

厂子方法情势中的Creator退化一下

看完上述代码,会体会到概括工厂和工厂方法情势是有很大相似性的了吧,从某个角度来讲,可以认为简单工厂就是工厂方法情势的一种特例,因而它们的本色是相近的,也就相差为奇了。

  1. 对设计基准的突显

厂子方法情势很好的体现了“依赖倒置原则”。

倚重倒置原则告诉我们“要倚重抽象,不要借助于现实类”,简单点说就是:不可能让高层组件倚重于低层组件,而且不管高层组件依旧低层组件,都应有依靠于肤浅

譬如说后面的言传身教,实现客户端请求操作的ExportOperate就是高层组件;而现实贯彻多少导出的目的就是低层组件,比如ExportTxtFile、ExportDB;而ExportFileApi接口就一定于是充裕抽象。

对于ExportOperate来说,它不关注具体的兑现情势,它只是“面向接口编程”;对于具体的落实的话,它只关注自己“如何实现接口”所要求的效率。

这就是说倒置的是何许啊?倒置的是以此接口的“所有权”。事实上,ExportFileApi接口中定义的效益,都是由高层组件ExportOperate来指出的渴求,也就是说接口中的功用,是高层组件需要的法力。可是高层组件只是提议要求,并不关注什么贯彻,而低层组件,就是来的确兑现高层组件所要求的接口功用的。因而看起来,低层实现的接口的所有权并不在底层组件手中,而是倒置到高层组件去了

  1. 何时拔取工厂方法模式

指出在如下意况中,采取工厂方法情势:

比方一个类需要创立某个接口的靶子,可是又不领会具体的贯彻,这种景色可以选拔工厂方法格局,把创立对象的办事推迟到子类去实现。

要是一个类本身就梦想,由它的子类来创制所需的目的的时候,应该采纳工厂方法格局。

2.1 工厂方法形式来缓解##

用来化解上述问题的一个合理的缓解方案就是工厂方法形式。那么怎样是工厂方法情势呢?

  1. 厂子方法形式定义

概念一个用来创造对象的接口,让子类决定实例化哪一个类,Factory
Method使一个类的实例化延迟到其子类。

  1. 运用工厂方法形式来缓解的笔触

仔细分析下边的题目,实在在促成导出数据的政工职能对象里面,根本就不了解究竟要利用哪一类导出文件的格式,由此那一个目的本就不应有和切实的导出文件的对象耦合在联名,它只需要面向导出的文件对象的接口就好了

而是这样一来,又有新的题目时有暴发了:接口是不可以一贯运用的,需要动用具体的接口实现目的的实例

那不是自相顶牛呢?要求面向接口,不让和切实的贯彻耦合,不过又需要成立接口的实际实现目标的实例。怎么解决这么些顶牛呢?

厂子方法情势的化解思路很风趣,这就是不解决,运用无为而治的主意:不是索要接口对象啊,这就定义一个主意来创立;不过实际上它自己是不清楚什么样创建这一个接口对象的,没有提到,这就定义成抽象方法就好了,自己实现持续,这就让子类来兑现,这样这些目的自我就足以只是面向接口编程,而无需关注到底咋样创造接口对象了

相关文章