研磨设计模式之工厂方法模式(Factory Method)-模式讲解2

2019-03-08 16:27|来源: 网络

3.2  工厂方法模式与IoC/DI

IoC——Inversion of Control  控制反转
DI——Dependency Injection   依赖注入


1:如何理解IoC/DI
       要想理解上面两个概念,就必须搞清楚如下的问题:

  • 参与者都有谁?

  • 依赖:谁依赖于谁?为什么需要依赖?

  • 注入:谁注入于谁?到底注入什么?

  • 控制反转:谁控制谁?控制什么?为何叫反转(有反转就应该有正转了)?

  • 依赖注入和控制反转是同一概念吗?

       下面就来简要的回答一下上述问题,把这些问题搞明白了,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类的示意图,如图7所示:


图7  常规A使用C示意图

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



图8  A类不再主动创建C


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


图9  有IoC/DI容器后程序结构示意图


(9)依赖注入和控制反转是同一概念吗?
       根据上面的讲述,应该能看出来,依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度在描述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。
(10)小结一下:

       其实IoC/DI对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC/DI容器来创建并注入它所需要的资源了。
       这么小小的一个改变其实是编程思想的一个大进步,这样就有效的分离了对象和它所需要的外部资源,使得它们松散耦合,有利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。


2:工厂方法模式和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里面写代码的时候,凡是碰到了需要外部资源,那么就提供注入的途径,要求从外部注入,自己只管使用这些对象。
       再来看看工厂方法模式,如何实现上面同样的功能,为了区分,分别取名为A1和C1。这个时候在A1里面要使用C1对象,也不是由A1主动去获取C1对象,而是创建一个工厂方法,就类似于一个注入的途径;然后由子类,假设叫A2吧,由A2来获取C1对象,在调用的时候,替换掉A1的相应方法,相当于反向注入回到A1里面,示例代码如下:

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的思想是相似的,都是“主动变被动”,进行了“主从换位”,从而获得了更灵活的程序结构。


本文链接:研磨设计模式之工厂方法模式(Factory Method)-模式讲解2,转自:http://chjavach.iteye.com/blog/692793

相关问答

更多
  • 抽象工厂:比工厂模式更深一层,这回连工厂的实现类都不知道了,不同的人可以拿到不同的工厂类。所以抽象工厂类其实是一个做工厂的工厂类,可以产生出不同的工厂类。 简单的说如果把“用工厂生成对象”这层关系当做一级生成关系,那么抽象工厂方法就是拥有二级生成关系的工厂方法。如果实际环境更复杂,可以三级 甚至 四级,所以你别想的太复杂,就这么简单。 设计模式这本书写的不错,不过请启发性的看他,别深究,因为: 设计模式成书较早那个时候程序员对程序的理解还保持在代码能编译通过就好,所以你会发现有很多特性在现在看起来很白痴,似 ...
  • 这不是GOF创作模式。 它是抽象工厂模式的变体,有时称为参数化工厂模式 。 参数化工厂根据传递给create方法的参数(通常是Id或类型说明符)创建不同的对象。 你的例子中的GOF工厂方法就是这样的(只是一个例子......没有意义......) struct AreaCalculator { virtual double calculateArea() = 0; }; struct CircleCalculator { CircleCalculator(const Circle& cir ...
  • 最简单的方法是使用单个静态字段,并在已创建映射时返回该值。 代码未经测试,但我认为这个概念很简单: public class MapCreator{ private static Map theOnlyMap; public synchronized Map createMap(char mapType){ if( theOnlyMap != null ) return theOnlyMap; switch(mapType){ case ...
  • 我不确定工厂方法设计模式在函数式编程中的作用程度。 模式的目标是隐藏对象的创建,以便您可以仅使用对象的抽象表示。 当您以功能方式使用F#时,您将在大多数时间使用具体表示。 例如,这使您能够在滑板类型上进行模式匹配。 当然,F#允许您将功能风格与面向对象的风格相结合。 出于某些目的,OO风格在F#中运行良好。 在这种情况下,您的方法看起来很合理。 您的工厂方法可以采用具体类型(例如,区分联合)作为参数,而不是字符串。 然后工厂的任务是从具体表示构建抽象表示: // Abstract representatio ...
  • 工厂方法已修复 - 您无法在运行时更改它。 抽象工厂允许您使用不同工厂创建对象,这可以在运行时根据某些条件进行选择。 Button button = WinButtonFactory.create(); //will always be a "windows button" Button button = buttonFactory.create(); 像这样的第二个可以是WinButtonFactory extends ButtonFactory或者MacOSXButtonFactory extends ...
  • 为什么不把工厂组成为Creator类而不是用抽象方法表达它呢? 因为在这种情况下,客户端“知道”要实例化哪个Factory类,这会破坏模式的整体目的。 整个想法是,客户不知道什么是工厂,它只是调用一个Abstract类。 假设你将一些第三方工厂提供者插入到你的框架中,并且只要达到目的,你就不知道他们实例化了什么(也不关心)。 Why not composing the Factory into the Creator class instead of expressing it using an abstr ...
  • 使用工厂方法模式的优点是,您可以将类的创建的业务逻辑与类的实际逻辑分离,因为如果您没有该工厂方法,那么您在系统上添加的每个类都需要具有内部的工厂方法,当你必须改变一些关于创建的东西时,你可能必须处理所有这些类(对于开放封闭原则不利) The advantage of using the factory method pattern is that you decouple the business logic of creation of a class from the actual logic of t ...
  • 我知道工厂方法模式有更多我缺少的东西但是依靠继承的优势组合,我找到了以下方法,在同一场景中解决同样的问题 使用Factory Method模式而不是普通旧组合时,您可以获得以下几个优点: 1.关注点分离和开放原则 :为每个相关的对象组创建一个工厂子类。 此工厂子类仅负责创建属于特定组的那些产品。 ABCProductFactory只关注创建ABCProduct1 , ABCProduct2等CDEProductFactory只关注创建CDEProduct1 , CDEProduct2等。 对于每个新产品组, ...
  • 遗憾的是,您正在阅读的链接并未提供该模式的实际示例。 实际上,根据原始的GoF设计模式,这种模式称为抽象工厂 (工厂方法是一种不同的模式)。 当您拥有可以创建一系列对象的工厂时,将使用抽象工厂模式。 例如,你可以拥有和AbstractGUIFactory ,它可以有方法createButton(), createWindow(), createTitleBar等。然后,你将拥有像WindowsGUIFactory, MacGUIFactory, MotifGUIFactory等具体的工厂,每个工厂都会产生B ...
  • 工厂的基本思想是函数返回从已知基类派生的堆分配对象。 因此,在你的情况下,你可能会在你的主应用程序中有一些代码调用dll中的工厂,找回未知动态类型和实现的对象,但你会知道它满足特定基类的API要求。 然后通过该接口使用该对象,并通过其大概的虚拟析构函数将其删除。 取决于实际派生类的选择方式,工厂方法有多种类型,但通常它会检查函数的一些输入,IO流或XML结构等,并计算出适当的类型。 工厂是否在dll中对这里的整体模型没有什么影响,但它确实可以更容易地更新派生对象的列表和实现,而无需重新编译应用程序。 有关更 ...