工厂方法模式(Factory Method Pattern)理论篇

Posted 古月书斋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了工厂方法模式(Factory Method Pattern)理论篇相关的知识,希望对你有一定的参考价值。

一、什么是工厂方法模式

工厂方法模式 属于类的创建型模式,它又被称为 多态工厂模式 ( Factory Method  Pattern )   。  工厂方法模式  的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。 核心工厂类 不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责实现一些通用的方法和定义一些具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

二 、模式结构和说明

        工厂方法模式的结构如图3所示:

                     图1 工厂方法模式结构示意图 Product         定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。 ConcreteProduct         具体的Product接口的实现对象。 Creator         创建器,声明工厂方法,工厂方法通常会返回一个Product类型的实例对象,而且多是抽象方法。也可以在Creator里面提供工厂方法的默认实现,让工厂方法返回一个缺省的Product类型的实例对象。 ConcreteCreator         具体的创建器对象,覆盖实现Creator定义的工厂方法,返回具体的Product实例。

三、 工厂方法模式示例代码

(1) 先看看Product的定义,示例代码如下:
/**
 * 工厂方法所创建的对象的接口
 */
public interface Product 
    //可以定义Product的属性和方法

(2) 再看看具体的Product的实现对象,示例代码如下: 
/**
 * 具体的Product对象
 */
public class ConcreteProduct implements Product 
    //实现Product要求的方法

(3)接下来看看创建器的定义,示例代码如下: 

/**
 * 创建器,声明工厂方法
 */
public abstract class Creator 
    /**
     * 创建Product的工厂方法
     * @return Product对象
     */
    protected abstract Product factoryMethod();
    /**
     * 示意方法,实现某些功能的方法
     */
    public void someOperation() 
       //通常在这些方法实现中,需要调用工厂方法来获取Product对象
       Product product = factoryMethod();
    

(4) 再看看具体的创建器实现对象,示例代码如下: 
/**
 * 具体的创建器实现对象
 */
public class ConcreteCreator extends Creator 
    protected Product factoryMethod() 
       //重定义工厂方法,返回一个具体的Product对象
       return new ConcreteProduct();
    

四、  认识工厂方法模式

4.1、工厂方法模式的功能         工厂方法的主要功能是让父类在不知道具体实现的情况下,完成自身的功能调用,而具体的实现延迟到子类来实现。          这样在设计的时候,不用去考虑具体的实现,需要某个对象,把它通过工厂方法返回就好了,在使用这些对象实现功能的时候还是通过接口来操作,这非常类似于IoC/DI的思想,这个在后面给大家稍详细点介绍一下。 4.2、实现成抽象类         工厂方法的实现中,通常父类会是一个抽象类,里面包含创建所需对象的抽象方法,这些抽象方法就是工厂方法。          这里要注意一个问题,子类在实现这些抽象方法的时候,通常并不是真的由子类来实现具体的功能,而是在子类的方法里面做选择,选择具体的产品实现对象。         父类里面,通常会有使用这些产品对象来实现一定的功能的方法,而且这些方法所实现的功能通常都是公共的功能,不管子类选择了何种具体的产品实现,这些方法的功能总是能正确执行 4.3、实现成具体的类         当然也可以把父类实现成为一个具体的类,这种情况下,通常是在父类中提供获取所需对象的默认实现方法,这样就算没有具体的子类,也能够运行。          通常这种情况还是需要具体的子类来决定具体要如何创建父类所需要的对象。也把这种情况称为工厂方法为子类提供了挂钩,通过工厂方法,可以让子类对象来覆盖父类的实现,从而提供更好的灵活性。 4.4、工厂方法的参数和返回         工厂方法的实现中,可能需要参数,以便决定到底选用哪一种具体的实现。也就是说通过在抽象方法里面传递参数,在子类实现的时候根据参数进行选择,看看究竟应该创建哪一个具体的实现对象。          一般工厂方法返回的是被创建对象的接口对象,当然也可以是抽象类或者一个具体的类的实例。 4.5、谁来使用工厂方法创建的对象         这里首先要搞明白一件事情,就是谁在使用工厂方法创建的对象?         事实上,在工厂方法模式里面,应该是Creator中的其它方法在使用工厂方法创建的对象,虽然也可以把工厂方法创建的对象直接提供给Creator外部使用,但工厂方法模式的本意,是由Creator对象内部的方法来使用工厂方法创建的对象,也就是说,工厂方法一般不提供给Creator外部使用。         客户端应该是使用Creator对象,或者是使用由Creator创建出来的对象。对于客户端使用Creator对象,这个时候工厂方法创建的对象,是Creator中的某些方法使用。对于使用那些由Creator创建出来的对象,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。分别举例来说明。      客户端使用Creator对象的情况          比如《 工厂方法模式(Factory Method Pattern) 应用篇(上)  》的示例,对于“实现导出数据的业务功能对象”的类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创建的对象 ,一般客户端不直接使用工厂方法。 当然也可以直接把工厂方法暴露给客户端操作,让客服端直接使用工厂方法,但是一般不这么做,关于此可参照《 工厂方法模式(Factory Method Pattern)应用篇(直接使用工厂方法) 》。 4.6、工厂方法模式的调用顺序示意图         由于客户端使用Creator对象有两种典型的情况,因此调用的顺序示意图也分做两种情况, 先来看客户端使用Creator对象时候的调用顺序示意图,如图5所示:

              图5 客户端使用Creator对象的调用顺序示意图 接着看看客户端使用由 Creator创建出来的对象 情况的调用顺序示意图,如图6所示:

             图6   客户端使用由Creator创建出来的对象的调用顺序示意图 4.7、工厂方法模式和IoC/DI的关系       从某个角度讲, 工厂方法模式 IoC/DI 的思想很类似。更多内容 请阅读《 工厂方法模式和IoC/DI的关系 五、  使用示例 5.1、普通工厂方法模式的使用 关于此请参照《 工厂方法模式(Factory Method Pattern) 应用篇(上) 5.2、参数化工厂方法模式的使用   所谓 参数化工厂方法 指的就是:通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象,这种情况就被称为参数化工厂方法。当然工厂方法创建的不同的产品必须是同一个Product类型的。 关于此请参照《 工厂方法模式(Factory Method Pattern) 应用篇(下) 六、 工厂方法模式的优缺点   6.1、可以在不知具体实现的情况下编程     工厂方法模式可以让你在实现功能的时候,如果需要某个产品对象,只需要使用产品的接口即可,而无需关心具体的实现。选择具体实现的任务延迟到子类去完成。 6.2、更容易扩展对象的新版本       工厂方法给子类提供了一个挂钩,使得扩展新的对象版本变得非常容易。比如上面示例的参数化工厂方法实现中,扩展一个新的导出Xml文件格式的实现,已有的代码都不会改变,只要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。     另外这里提到的挂钩,就是我们经常说的钩子方法(hook),这个会在后面讲模板方法模式的时候详细点说明。 6.3、连接平行的类层次      工厂方法除了创造产品对象外,在连接平行的类层次上也大显身手。这个在前面已经详细讲述了。 具体产品对象和工厂方法的耦合性     在工厂方法模式里面,工厂方法是需要创建产品对象的,也就是需要选择具体的产品对象,并创建它们的实例,因此具体产品对象和工厂方法是耦合的。 七、  思考工厂方法模式 7.1、工厂方法模式的本质   工厂方法模式的本质:延迟到子类来选择实现。    仔细体会前面的示例,你会发现,工厂方法模式中的工厂方法,在真正实现的时候,一般是先选择具体使用哪一个具体的产品实现对象,然后创建这个具体产品对象的示例,然后就可以返回去了。也就是说,工厂方法本身并不会去实现产品接口,具体的产品实现是已经写好了的,工厂方法只要去选择实现就好了。   有些朋友可能会说,这不是跟简单工厂一样吗?   确实从本质上讲,它们是非常类似的,具体实现上都是在“选择实现”。但是也存在不同点,简单工厂是直接在工厂类里面进行“选择实现”;而工厂方法会把这个工作延迟到子类来实现,工厂类里面使用工厂方法的地方是依赖于抽象而不是具体的实现,从而使得系统更加灵活,具有更好的可维护性和可扩展性。   其实如果把工厂模式中的Creator退化一下,只提供工厂方法,而且这些工厂方法还都提供默认的实现,那不就变成了简单工厂了吗?比如把 工厂方法模式(Factory Method Pattern) 应用篇(下) 的例子代码拿过来再简化一下,你就能看出来,写得跟简单工厂是差不多的,示例代码如下: 

看完上述代码,会体会到简单工厂和工厂方法模式是有很大相似性的了吧,从某个角度来讲,可以认为简单工厂就是工厂方法模式的一种特例,因此它们的本质是类似的,也就不足为奇了。 7.2、对设计原则的体现   工厂方法模式很好的体现了“依赖倒置原则”。    依赖倒置原则告诉我们“要依赖抽象,不要依赖于具体类”,简单点说就是:不能让高层组件依赖于低层组件,而且不管高层组件还是低层组件,都应该依赖于抽象。    比如前面的示例,实现客户端请求操作的ExportOperate就是高层组件;而具体实现数据导出的对象就是低层组件,比如ExportTxtFile、ExportDB;而ExportFileApi接口就相当于是那个抽象。   对于ExportOperate来说,它不关心具体的实现方式,它只是“面向接口编程”;对于具体的实现来说,它只关心自己“如何实现接口”所要求的功能。   那么倒置的是什么呢?倒置的是这个接口的“所有权”。事实上,ExportFileApi接口中定义的功能,都是由高层组件ExportOperate来提出的要求,也就是说接口中的功能,是高层组件需要的功能。但是高层组件只是提出要求,并不关心如何实现,而低层组件,就是来真正实现高层组件所要求的接口功能的。因此看起来,低层实现的接口的所有权并不在底层组件手中,而是倒置到高层组件去了。 7.3、何时选用工厂方法模式    建议在如下情况中,选用工厂方法模式: A :如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类去实现 B :如果一个类本身就希望,由它的子类来创建所需的对象的时候,应该使用工厂方法模式 八、 相关模式 工厂方法模式 抽象工厂模式      这两个模式可以组合使用,具体的放到抽象工厂模式中去讲。   工厂方法模式 模板方法模式      这两个模式外观类似,都是有一个抽象类,然后由子类来提供一些实现,但是工厂方法模式的子类专注的是创建产品对象,而模板方法模式的子类专注的是为固定的算法骨架提供某些步骤的实现。     这两个模式可以组合使用,通常在模板方法模式里面,使用工厂方法来创建模板方法需要的对象。

以上是关于工厂方法模式(Factory Method Pattern)理论篇的主要内容,如果未能解决你的问题,请参考以下文章

设计模式:工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)

Factory Method 工厂方法模式

工厂方法模式-Factory Method

工厂方法模式(Factory Method)

设计模式——工厂方法模式(Factory Method)

设计模式工厂方法模式(Factory Method)