工厂模式
工厂模式在“创造模式”中与其它模式相比,有其难点,困难之处并不是工厂模式的定义本身,令人困惑的地方在于为什么要使用工厂模式,或者说是必须使用工厂的理由。
工厂模式的定义
为创建某个目标类实例定义一个接口,由此接口的类实现对目标类的实例化。(此接口即工厂接口,IFactory)
(Define an interface for creating an object, but let the classes that implement the interface decide which class to instantiate. )
工厂(方法)模式派生目标类的实例化在其工厂接口子类中
(The Factory method lets a class defer instantiation to subclasses.)
对于使用工厂模式的疑问在于,产品类所定义产品实体是在客户逻辑中真正使用的目标,如果产品类已经抽象,使用者只需要将产品的子类实例化后使用产品的接口或父类,为什么一定要使用工厂类,多此一举。
即使使用工厂类,实际上是工厂的子类去实例化具体的产品,对于使用者来说,去判断使用哪一个工厂和去判断使用哪一个产品没有本质的区别,为什么在设计的时候还需要加入工厂的接口,和工厂的继承类,增加复杂度呢。
关键的问题在于工厂模式定义中的最后一句,工厂的子类决定如何实现子产品,实现局域化和特性化的产品。
产品的实例化,指的是子产品的实例化,子产品在实现的时候,是不是需要有特性化和局域化的需求,成为是否使用工厂模式的判断标准。
更简单的说,如果所有的产品在的实例化只是一个简单的new,可以解决,那使用工厂就完全是多此一举。而各种产品,虽然可以维持统一接口, 但在实例化时是需要不同的,甚至复杂的处理,或者更复杂的逻辑处理,那么,工厂的目的就是,将复杂的不同的实例化逻辑统一成统一的接口,来屏蔽使用者对于实例化产品是对其复杂逻辑的理解与实现,而统一使用简单的工厂接口。
就以图中类为例
class Light{ | ||
public: | ||
Light(){} | ||
virtual void TurnOn() = 0; | ||
} | ||
class BulbLight : public { | ||
public: | ||
BulbLight() | ||
void TurnOn() { | ||
// power 5v | ||
} | ||
} | ||
class TubeLight : public { | ||
public: | ||
TubeLight(){} | ||
void TurnOn(){ | ||
// power 220v | ||
} | ||
} |
如果做两个工厂类BulbFactory和TubeFactory对BulbLight和TubeLight进行实例化,使用者仍然需要决定并实现Factory的实例,这同实现具体的Light类没有区别,也没有益处,反而增加工作量和复杂度。
然而,Light子类的构造变得复杂时,情况变不一样了
class BulbLight : public { | ||
Power power; | ||
public: | ||
BulbLight(Power bettery) : power(bettery) {} | ||
void Fill(Gas current_gas); | ||
void TurnOn() { | ||
// power 5v | ||
} | ||
} | ||
class TubeLight : public { | ||
Cable wire; | ||
public: | ||
TubeLight(Cable power) : wire(power) {} | ||
void TurnOn(){ | ||
// power 220v | ||
} | ||
} |
对于使用者来说,不去向BulbLight和TubeLight的设计者(文档)了解实例化的细节,将很难使用两个子类
这将是Factory的优势所在,Light子类的设计者,同时提供Factory接口的产品实例化方法,屏蔽了产品本身实例化的复杂逻辑
如果实例化之后还有其他本地化的逻辑,如Fill之类,工厂模式的特性会更具体的体现出来。
对抽象工厂模式的理解,主要问题在与工厂模式的区别
仅就定义图和继承关系来看,二者基本上没有区别
但两个模式的定义给出了非常清晰的解释,简单的说,他们所关注的问题层次完全不同
抽象工厂的定义
为创建一组相关性目标实例提供接口而无须指定具体目标类
(Provide an interface for creating families of related or dependent objects without specifying their concrete classes.)
结构示例图
工厂模式,关注的是特定产品实例化的简化接口,抽象工厂关注的是产品集合的接口化和工厂本身的统一管理
最简单的实例可以考虑为,将一个系统或程序移植到另一个环境或操作系统时所遇到的问题。
可以想象,在不使用抽象工厂的场景中,所有目标实体的实例化都使用new去具体实现
那么,遇到系统移植的需要时,有两种方案可供选择
一是修改所有实体类的代码,使其适应新系统,新环境,这样可以保证使用者(实例化的地方)使用逻辑不变
第二种方法是设计一套新的实体类,使用不同的命名,替代原来使用逻辑中的实例化代码,全部使用新的类名
这两种方法无论哪种,其修改工作量都不容小视,代码的安全(运行)性也受到威胁
抽象工厂模式意图解决这类问题中实体类集体替换的问题
对于上面两种方法的修改是,使用抽象工厂的统一接口创建目标对象,实例的使用者实例化时只使用抽象工厂接口
这种方法在第一次系统移植时工作量会有所增加(与以上两种方法相比),而在第三次或之后的移植中会体现出优势
而如果,在系统原始设计时即使用抽象工厂模式,则对新系统适应性的修改只体现在新建一批目标类上,从而受益良多
抽象工厂与工厂模式并不重叠,也不矛盾,他们试图解决不同的问题,不同层次的问题
工厂本身专注于如何简化可替代产品的实例化过程,抽象工厂希望通过对工厂的接口化管理,简化产品集群的管理
抽象工厂的名称容易使概念混乱,其意义更像实际生活中的企业集团
集团可以兼并制造类似产品的多个工厂,工厂生产的规格和标准在集团内统一,即抽象工厂的接口(IAbstractFactory)
从这个意义上讲,命名这个模式为集团模式可能更加容易理解一些
建造者模式中的两个概念,指挥者和建造者定义比较清晰并不难理解。
模式定义
将创建复杂目标实体的工作分离,保持相同制造流程的情况下,创建不同的目标实例
Separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.
施工模式中的一个关键概念是目标实体的复杂性,模式的目的就是将复杂实体的构建过程抽象和统一起来,可以由一个预先定义好的流水线管理
如示例图中所描述,复杂实体作为最后产出物,从创建者的getResult获取
很有趣的是,建造者的接口中并不定义获取产品getResult的接口,原因是施工模式关注的只是对制作过程的管理,而不是对产品本身继承性的管理(产品的管理,由两个工厂类考虑)
指挥者对构造过程(流水线)的管理在construct中实现,建造者的实体类负责具体实体的创建
使用者选择具体需要的建造者,同时使用预先定义好的指挥者(构造流水construct实际上可以是静态的)
最终的产品实例是从实际创建类中直接获取,因为与建造过程无关