抽象工厂
Intent
提供一个接口,用于在不知道具象(concrete)类的情况下,创建一族(families)相关或互存的对象。
Also Known As
Kit
Motivation
设想一个UI工具包(toolkit),支持多种观感(look-and-feel)标准,诸如Motif和Presentation Manger。不同的观感给UI饰件”widgets”(比如滚动条,窗口,按钮)定义了不同的表象(appearances)和行为(behaviors)。为了支持在不同的观感标准之间方便地移植或者切换,应用程序不能只为某一种特定的观感而将饰件硬编码来实例化该类特定观感的饰件,那样的话就很难轻易改变观感了。
这个问题可以通过定义抽象的WidgetFactory类来解决,该类宣告了一个创建各种基本饰件的接口。对每一种饰件还有一个抽象类,再由具象子类来实现特定的观感标准。WidgetFactory的接口有一个操作可以为每个抽象饰件类返回新的饰件对象。Client调用这些操作来获得饰件实例,但是Client并不关心具体使用了那个具象类。这样Client就跟具体的观感保持了相对独立性。
对每一种观感标准,有一个具象子类WidgetFactory。每一个子类实现了操作可以用来创建相应的饰件。Client创建饰件时完全通过WidgetFactory接口即可,不需要知道实现特定观感的饰件的是哪个类。换句话说,Client只需要向抽象类定义的接口提交请求,而不是特定的具象类。
WidgetFactory还强化(enforce)了具象饰件类之间的依赖关系。Motif scroll bar应该跟Motif button和Motif text editor成套使用,这个约束条件作为使用MotifWidgetFactory的衍生结果(consequence)自动得到了体现(enforce)。
Applicability
在以下情况下可以考虑使用抽象类模式:
1。一个系统需要保持产品创建,编辑和表达的方式的相对独立性
2。一个系统需要被配置成可以灵活支持多族产品中的一族。
3。一族相关产品对象被设计成成套使用,你需要(在设计和架构上: 如来熊理解)强化这种约束条件。
4。你想提供一个产品的类库,但是你只想揭示它们的接口,而不是实现。
Structure
Participants
1. AbstractFactory (WidgetFactory)
为创建AbstractProduct对象的操作宣告了一个接口
2. ConcreteFactory (MotifWidgetFactory, PMWidgetFactory)
实现了创建ConcreteProduct对象的操作
3. AbstractProduct (Window, ScrollBar)
为一类产品对象宣告了一个接口
4. ConcreteProduct (MotifWindow, MotifScrollBar)
定义了一个产品类,该产品类由相应的ConcreteFactory来创建
实现了AbstractProduct 接口
5. Client
只直接使用AbstractFactory和AbstractProduct 这两个类宣告的接口。
Collaborations
1。 正常情况下,ConcreteFactory类的单个实例是在运行时创建的。这个ConcreteFactory 创建产品对象有特殊的实现。要创建不同的产品对象,Client要使用不同的ConcreteFactory。
2。 AbstractFactory 把产品对象的创建和它的ConcreteFactory 子类区别开来。
Consequences
抽象工厂模式有以下好处和坏处:
好处:
1。对Client隐藏了具象类。
(如来熊想:隐藏到哪一层?好像只隐藏了具象产品类。具象工厂类的隐藏则要看如何界定Client。如果Client只是Consumer的概念,则可以认为具象工厂类也被隐藏了。)
2。使得产品族的切换变得容易。
3。加强了产品之间的一致性。
(如来熊想:本文的例子中,可以看出,相同观感的饰件成套使用的约束条件已经从设计和架构上由抽象工厂模式给保证了,不需要Client再刻意地去维持一致性,大大降低了Client的脑力负担。)
坏处:
1。支持新的产品将变得困难。
(如来熊想:本文的例子里面,抽象工厂类里为每一种产品宣告了一个接口;要想支持新的饰件产品,就要增加抽象工厂类的接口,不可避免要对整个Framework进行改动。因为使用抽象工厂类自然引入了一个Framework,所以支持新的产品种类变得十分繁琐,特别是会引起接口的改变。有关Client就必须重新编译。所以使用抽象工厂类之前一定要经过彻底地domain分析和穷举。但是支持新的观感就非常简单了。失之东隅,收之桑榆嘛。)
(如来熊提示: 这个坏处其实可以通过参数化抽象工厂类接口的方法来克服。这个克服的方法更加灵活,但是不太安全。具体方法是: 在创建对象的操作方法中增加参数,通过该参数指定要创建的对象种类。可以是类修饰符, 一个整型,一个字符串,或者任何其他可以用来区分产品种类的类型。事实上,采用这种参数化方法之后,抽象工厂类只需要一个带有参数的“Make”操作方法就足够了。这个技术一般使用在Prototype模式和基于类的抽象工厂模式中。)
实现
在实现抽象工厂模式的时候,有一些有用的技巧:
1。把具象工厂类实现为一个单件(singleton):
一个应用程序对一族产品一般只需要一个具象工厂类的。所以通常最好实现成单件模式。