工厂模式

工厂模式在“创造模式”中与其它模式相比,有其难点,困难之处并不是工厂模式的定义本身,令人困惑的地方在于为什么要使用工厂模式,或者说是必须使用工厂的理由。


工厂模式的定义

为创建某个目标类实例定义一个接口,由此接口的类实现对目标类的实例化。(此接口即工厂接口,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.)


设计模式解析_第1张图片


对于使用工厂模式的疑问在于,产品类所定义产品实体是在客户逻辑中真正使用的目标,如果产品类已经抽象,使用者只需要将产品的子类实例化后使用产品的接口或父类,为什么一定要使用工厂类,多此一举

即使使用工厂类,实际上是工厂的子类去实例化具体的产品,对于使用者来说,去判断使用哪一个工厂和去判断使用哪一个产品没有本质的区别,为什么在设计的时候还需要加入工厂的接口,和工厂的继承类,增加复杂度呢。


关键的问题在于工厂模式定义中的最后一句,工厂的子类决定如何实现子产品,实现局域化和特性化的产品。


产品的实例化,指的是子产品的实例化,子产品在实现的时候,是不是需要有特性化和局域化的需求,成为是否使用工厂模式的判断标准


更简单的说,如果所有的产品在的实例化只是一个简单的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.)


结构示例图


设计模式解析_第2张图片


工厂模式,关注的是特定产品实例化的简化接口,抽象工厂关注的是产品集合的接口化和工厂本身的统一管理

最简单的实例可以考虑为,将一个系统或程序移植到另一个环境或操作系统时所遇到的问题。


可以想象,在不使用抽象工厂的场景中,所有目标实体的实例化都使用new去具体实现

那么,遇到系统移植的需要时,有两种方案可供选择

一是修改所有实体类的代码,使其适应新系统,新环境,这样可以保证使用者(实例化的地方)使用逻辑不变

第二种方法是设计一套新的实体类,使用不同的命名,替代原来使用逻辑中的实例化代码,全部使用新的类名

这两种方法无论哪种,其修改工作量都不容小视,代码的安全(运行)性也受到威胁


抽象工厂模式意图解决这类问题中实体类集体替换的问题

对于上面两种方法的修改是,使用抽象工厂的统一接口创建目标对象,实例的使用者实例化时只使用抽象工厂接口

这种方法在第一次系统移植时工作量会有所增加(与以上两种方法相比),而在第三次或之后的移植中会体现出优势

而如果,在系统原始设计时即使用抽象工厂模式,则对新系统适应性的修改只体现在新建一批目标类上,从而受益良多


抽象工厂与工厂模式并不重叠,也不矛盾,他们试图解决不同的问题,不同层次的问题

工厂本身专注于如何简化可替代产品的实例化过程,抽象工厂希望通过对工厂的接口化管理,简化产品集群的管理

抽象工厂的名称容易使概念混乱,其意义更像实际生活中的企业集团

集团可以兼并制造类似产品的多个工厂,工厂生产的规格和标准在集团内统一,即抽象工厂的接口(IAbstractFactory)

从这个意义上讲,命名这个模式为集团模式可能更加容易理解一些


建造者模式中的两个概念,指挥者和建造者定义比较清晰并不难理解。

模式定义


将创建复杂目标实体的工作分离,保持相同制造流程的情况下,创建不同的目标实例

Separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.


施工模式中的一个关键概念是目标实体的复杂性,模式的目的就是将复杂实体的构建过程抽象和统一起来,可以由一个预先定义好的流水线管理


设计模式解析_第3张图片

如示例图中所描述,复杂实体作为最后产出物,从创建者的getResult获取

很有趣的是,建造者的接口中并不定义获取产品getResult的接口,原因是施工模式关注的只是对制作过程的管理,而不是对产品本身继承性的管理(产品的管理,由两个工厂类考虑)


指挥者对构造过程(流水线)的管理在construct中实现,建造者的实体类负责具体实体的创建

使用者选择具体需要的建造者,同时使用预先定义好的指挥者(构造流水construct实际上可以是静态的)

最终的产品实例是从实际创建类中直接获取,因为与建造过程无关