• 【创建型】Abstract Factory模式 & Factory Method模式


        本文主要介绍简单工厂模式与抽象工厂模式。以下如有叙述不当之处,欢迎批评指正,欢迎交流探讨。

    一:简单工厂模式

        在23种设计模式中,简单工厂模式其实是不在列的,但理解该模式对于抽象工厂模式的思想理解是有帮助的。就纯粹从字面上理解,简单工厂模式就是一种简单版的工厂模式。在日常开发过程中,相信绝大多数人都使用过。它主要是在设计上将一种产品或一系列具有相互关系的产品的创建工作,由专门设计好的类(即:工厂类)统一提供相关接口来完成。这样,在上下文中所面对的产品类的创建工作将是“隐式的”,不会直接产生对某个或某些具体产品类的依赖。

        简单工厂模式是指为创建一系列相关或相互依赖的对象提供接口,使得上下文脱离对具体产品类的直接依赖。类关系图参考如下:

        根据上面关系图,模式的编码结构参考如下:

     1 namespace simple_factory
     2 {
     3     /******************************************************************************
     4      * create   : (jacc.kim) [5-15-2016]
     5      * summary  : 产品A
     6     ******************************************************************************/
     7     class IAbstractProductA
     8     {
     9     public:
    10         enum class eType {};
    11     };//class IAbstractProductA
    12 
    13     class ConcreteProductA1 : public IAbstractProductA {};
    14     class ConcreteProductA2 : public IAbstractProductA {};
    15 
    16     /******************************************************************************
    17      * create   : (jacc.kim) [5-15-2016]
    18      * summary  : 产品B
    19     ******************************************************************************/
    20     class IAbstractProductB
    21     {
    22     public:
    23         enum class eType {};
    24     };//class IAbstractProductB
    25 
    26     class ConcreteProductB1 : public IAbstractProductB {};
    27     class ConcreteProductB2 : public IAbstractProductB {};
    28 
    29     /******************************************************************************
    30      * create   : (jacc.kim) [5-15-2016]
    31      * summary  : 简单工厂
    32     ******************************************************************************/
    33     class SimpleFactory
    34     {
    35     public:
    36         IAbstractProductA* createProductA(const IAbstractProductA::eType) {}    // 参数可选
    37         IAbstractProductB* createProductB(const IAbstractProductB::eType) {}    // 参数可选
    38     };//class SimpleFactory
    39 
    40 }//namespace simple_factory
    简单工厂模式编码结构参考

        简单工厂的一个明显的缺点是一旦具体产品的种类变得越来越多时,则工厂将会变得庞大且复杂,后期维护将会越来越累。

    二:抽象工厂模式

        与简单工厂模式类似,抽象工厂模式类似也是为创建一系列相关或相互依赖的产品提供统一的接口,并且不需要指定具体的产品类。

        与简单工厂模式不同的是不同系列类型的产品是由具体的工厂类来负责创建,从而避免简单工厂模式中,工厂类的实现分支过度复杂的问题。类关系图参考如下:

        由图知,不同的产品系列将是由不同的具体工厂类来实例化。模式编码结构参考如下:

     1 namespace abstract_factory
     2 {
     3     /******************************************************************************
     4      * create   : (jacc.kim) [5-16-2016]
     5      * summary  : 产品A
     6     ******************************************************************************/
     7     class IAbstractProductA
     8     {
     9     public:
    10         enum class eType {};
    11     };//class IAbstractProductA
    12 
    13     class ConcreteProductA1 : public IAbstractProductA {};
    14     class ConcreteProductA2 : public IAbstractProductA {};
    15 
    16     /******************************************************************************
    17      * create   : (jacc.kim) [5-16-2016]
    18      * summary  : 产品B
    19     ******************************************************************************/
    20     class IAbstractProductB
    21     {
    22     public:
    23         enum class eType {};
    24     };//class IAbstractProductB
    25 
    26     class ConcreteProductB1 : public IAbstractProductB {};
    27     class ConcreteProductB2 : public IAbstractProductB {};
    28 
    29     /******************************************************************************
    30      * create   : (jacc.kim) [5-16-2016]
    31      * summary  : 工厂
    32     ******************************************************************************/
    33     class IAbstractFactory
    34     {
    35     public:
    36         IAbstractProductA* createProductA(/*const IAbstractProductA::eType*/) {}    // 参数可选
    37         IAbstractProductB* createProductB(/*const IAbstractProductB::eType*/) {}    // 参数可选
    38     };//class IAbstractFactory
    39 
    40     class ConcreteFactory1 : public IAbstractFactory {
    41     public:
    42         IAbstractProductA* createProductA(/*const IAbstractProductA::eType*/) { return new (std::nothrow) ConcreteProductA1(); }
    43         IAbstractProductB* createProductB(/*const IAbstractProductB::eType*/) { return new (std::nothrow) ConcreteProductB1(); }
    44     };//class ConcreteFactory1
    45 
    46     class ConcreteFactory2 : public IAbstractFactory {
    47     public:
    48         IAbstractProductA* createProductA(/*const IAbstractProductA::eType*/) { return new (std::nothrow) ConcreteProductA2(); }
    49         IAbstractProductB* createProductB(/*const IAbstractProductB::eType*/) { return new (std::nothrow) ConcreteProductB2(); }
    50     };//class ConcreteFactory2
    51 
    52 }//namespace abstract_factory
    抽象工厂模式编码结构参考

    三:工厂方法模式

        与抽象工厂模式类似,也是为了解决简单工厂模式中因具体产品种类过多而导致工厂的负担过重的问题。工厂方法模式是指:为创建具体产品对象定义统一的接口,并且接口的具体实现是由具体的子类来实现。即:具体的产品的实例化由对应的具体工厂来负责。形象点说就是,某个具体的工厂子类,负责创建对应的具体的产品对象。类关系图参考如下:

     

        工厂方法模式编码结构参考如下:

     1 namespace factory_method
     2 {
     3     /******************************************************************************
     4      * create   : (jacc.kim) [5-16-2016]
     5      * summary  : 产品
     6     ******************************************************************************/
     7     class IAbstractProduct {};
     8     class ConcreteProduct1 : public IAbstractProduct {};
     9     class ConcreteProduct2 : public IAbstractProduct {};
    10 
    11     /******************************************************************************
    12      * create   : (jacc.kim) [5-16-2016]
    13      * summary  : 工厂
    14     ******************************************************************************/
    15     class IAbstractFactory
    16     {
    17     public:
    18         virtual IAbstractProduct* createProduct() = 0;
    19     };//class IAbstractFactory
    20 
    21     class ConcreteFactory1 : public IAbstractFactory
    22     {
    23     public:
    24         virtual IAbstractProduct* createProduct() override {
    25             return new (std::nothrow) ConcreteProduct1();
    26         }
    27     };//class ConcreteFactory1
    28 
    29     class ConcreteFactory2 : public IAbstractFactory
    30     {
    31     public:
    32         virtual IAbstractProduct* createProduct() override {
    33             return new (std::nothrow) ConcreteProduct2();
    34         }
    35     };//class ConcreteFactory2
    36 
    37 }//namespace factory_method
    工厂方法模式编码结构参考

    四:几种模式总结

        a) 简单工厂模式与抽象工厂模式均可创建一系列相关或相互依赖的产品,产品的类型可以不同。但抽象工厂模式的工厂类中,创建具体产品对象时,是由相关的派生类工厂对象来负责。而简单工厂模式则是通过条件分支来分别处理;

        b) 工厂方法模式重点是负责同一类型系列的产品,产品类型会比较单一;

        c) 抽象工厂模式与工厂方法模式,在工厂类层面上,各个具体派生类的职责相比简单工厂模式会更加的清晰、明确;

        d) 不论是以上哪种模式,最终的目的都只有一个:保证客户端(即:以上图中的 Client)层面上的逻辑框架结构不需要修改就可以轻松切换各种具体产品对象的使用,而且最为最关键的是Client还不需要直接依赖于这些具体的产品对象类。

     

    五:高级话题

        目前比较主流的几种设计语言中,如Java、C#这类较C++晚出现的语言来说,像垃圾回收这样的机制,相信众多程序猿都会很自豪地说这么一句话:自动有了垃圾回收后,“妈妈”再也不用担心我写的代码会有内存泄漏了。其实这些语言还有一样利器,也是非常屌的,就是反射。相信许多c++程序猿都会经常做这么一件事,像上面工厂模式中,经常会不断地if else或case各种情况,然后创建各种对应的子产品对象。试想下,如果日后再新添加产品时,就得重新改一次工厂类的实现。再加一种产品,再改一次,........一直如此维护下去。而且一般项目中,可能会有非常非常多的工厂类,那维护就不是一般的累了。因此有经验的C++程序员此时会立刻想到反射。

        如果有人对RTTI产生过好奇的话,可能就会想到这么一个问题:我们平时写的类啊什么的,是保存在一个个文件中的,可为什么编译器就是能够把它们识别出来,并让程序正常有序地工作下去而不会出错?有兴趣的同学可以上网找找这方面的资料,总是有许多的。在此,主要是想说说C++中的“反射”(因为此处讲反射与本博客的主题算是有一定的关系)。C++虽说是一门非常高级的高级语言,但它却没有反射机制(不但没有反射机制,就连垃圾回收机制也是没有的)。可幸的是,C++也是一门威力十分强大的语言。就算没有,许多前辈们也总能想出办法来实现它们。就如没有垃圾回收,但我们可以实现智能指针,反射也一样。

        要实现反射,则首先必需要解决的一个问题是:类型 -------- 任意类型。我们不可能只为某一个类(系列)实现反射,那样根本就没有通用性,代码就不可利用,就没有任何大的意义。而C++中,类型无关性的编码,很自然的会想到template,思路到这,相信许多人一下子就豁然开然了。因此最基本的实现编码结构如下:

     1 /******************************************************************************
     2  * create   : (jacc.kim) [1-14-2016]
     3  * summary  : class GFReflector.custom product reflector.
     4 ******************************************************************************/
     5 template<typename TIDX, typename TProduct>
     6 class GFReflector
     7 {
     8 public:
     9     // create a new concrete produce instance.
    10     static TProduct*            createInstance(const TIDX& id);
    11 
    12 };//template<typename TIDX, typename TProduct> class GFReflector
    Reflector编码基本结构参考

        再接下来要做什么?很明显为了根据TIDX类型,我们要能够创建出对应的对象实例。而这部分的工作,可以转交给用户去实现(也必需交由用户去实现)。于是我们需要一张映射表,要能够根据具体TIDX,映射到如何创建出对应对象实例的表。此时自然就会想到函数指针、仿函数、function等等技术手段。因此,改进后的编码结构大致如下:

     1 /******************************************************************************
     2  * create   : (jacc.kim) [1-14-2016]
     3  * summary  : class GFReflector.custom product reflector.
     4 ******************************************************************************/
     5 template<typename TIDX, typename TProduct>
     6 class GFReflector
     7 {
     8 public:
     9     typedef typename TProduct* (*fpProductCreator)();
    10     typedef typename std::function<TProduct*()> GFProductCreator;
    11 
    12     // create a new concrete produce instance.
    13     static TProduct*            createInstance(const TIDX& id);
    14 
    15 public:
    16     GFReflector();
    17     GFReflector(const TIDX& id, fpProductCreator fpCreator);
    18     /*virtual */~GFReflector();
    19 
    20 private:
    21     GFReflector(const GFReflector&) DELETE_METHOD;
    22     GFReflector& operator=(const GFReflector&) DELETE_METHOD;
    23 
    24 private:
    25     typedef typename std::map<TIDX, GFProductCreator>   GFCreatorMap;
    26 
    27 private:
    28     static GFCreatorMap&        getAllCreator();
    29 
    30 };//template<typename TIDX, typename TProduct> class GFReflector
    Reflector编码结构参考

        最后,用户在使用上只需要简简单单地进行注册与反注册即可。比如(用法示例):

    1 // 示例一:
    2 typedef nsgf::GFReflector<eGHEventType, IGHEvent>   TEventReflector;
    3 
    4     TEventReflector::registe(eGHEventType::eComplex, [](){ return GFNEW CGHComplexEvent(); });
    5 
    6 // 示例二:
    7 typedef nsgf::GFReflector<eGHSkillType, IGHSkill>   TSkillReflector;
    8 
    9     TSkillReflector::registe(eGHSkillType::eRoleSkill, [](){ return GFNEW CGHRoleSkill(); });
    反射用法示例参考

        有了以上的反射机制,我们还可以实现一个通用的工厂类。以下是个人先前搭的一个框架中设计的一个通用工厂类的主要编码结构(仅供参考):

     1 /******************************************************************************
     2  * create   : (jacc.kim) [1-14-2016]
     3  * summary  : class GFProductFactory.custom product factory.
     4 ******************************************************************************/
     5 template<typename TIDX, typename TProduct>
     6 class GFProductFactory
     7 {
     8 public:
     9     typedef std::function<void()>                   GFRegister;
    10     typedef typename std::function<void(TProduct*)> GFProductDestroyer;
    11 
    12 public:
    13     // clear all instances of cache.(remove from memory really).
    14     void                        clearAllInstance();
    15 
    16 public:
    17     // create a new concrete produce instance.
    18     virtual TProduct*           createInstance(const TIDX& id);
    19 
    20     // (force) borrow a concrete produce instance.
    21     virtual TProduct*           borrowInstance(const TIDX& id);
    22 
    23     // return a concrete produce instance that can generate with borrowInstance() or createInstance() method.
    24     virtual bool                returnInstance(const TIDX& id, TProduct* pProduct);
    25 
    26 public:
    27     virtual ~GFProductFactory();
    28 
    29     // some code here........
    30 
    31 };//template<typename TIDX, typename TProduct> class GFProductFactory
    32 
    33 NSGF_END
    通用工厂类的主要编码结构参考

        该通用工厂类不但支持一般工厂模式的功能,还可以支持Cache功能。即:有些功能中,因为对象使用十分频繁,如果每次创建用完后就删除掉,那样将是十分耗性能的。比如:在游戏AI中,肯定会有大量的对象,Buff,技能........,此时cache功能就十分有用。从上面的通用工厂类设计中也可以看出,支持createInstance(); 也支持borrowInstance();与returnInstance();一般情况下,后面两个接口是成对使用的。当然如果是由createInstance()创建出来的对象也return给通用工厂类,Ok,完全没问题。因为它是一个简单意义上的Cache,没有任何其他逻辑上的约束。因此,使用上十分方便。其实有这样的一个通用工厂类,在一定意义上,可以完全取代 Flyweight 设计。

  • 相关阅读:
    urlrewrite地址重写的使用
    算法学习
    数据库之Case When
    速卖通返回503错误
    概述:软件开发工具
    c#将List&lt;T&gt;转换成DataSet
    表单域规范写法
    ant打包和jar包混淆
    Node.js文档和教程
    webpack开发和生产两个环境的配置详解
  • 原文地址:https://www.cnblogs.com/tongy0/p/5484366.html
Copyright © 2020-2023  润新知