设计模式之ABSTRACT FACTORY(抽象工厂)—对象创建型模式

来源:互联网 发布:舒尔特训练软件 编辑:程序博客网 时间:2024/04/30 12:14

1. 意图

       提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

2. 别名

       K i t

3. 动机

        考虑一个支持多种视感( l o o k - a n d - f e e l)标准的用户界面工具包,例如M o t i f和Presentation Manager。不同的视感风格为诸如滚动条、窗口和按钮等用户界面“窗口组件”

        定义不同的外观和行为。为保证视感风格标准间的可移植性,一个应用不应该为一个特定的视感外观硬编码它的窗口组件。在整个应用中实例化特定视感风格的窗口组件类将使得以后很难改变视感风格。为解决这一问题我们可以定义一个抽象的Wi d g e t F a c t o r y类,这个类声明了一个用来创建每一类基本窗口组件的接口。每一类窗口组件都有一个抽象类,而具体子类则实现了窗口组件的特定视感风格。对于每一个抽象窗口组件类, Wi d g e t F a c t o r y接口都有一个返回新窗口组件对象的操作。客户调用这些操作以获得窗口组件实例,但客户并不知道他们正在使用的是哪些具体类。这样客户就不依赖于一般的视感风格,如下页图所示。

        每一种视感标准都对应于一个具体的Wi d g e t F a c t o r y子类。每一子类实现那些用于创建合适视感风格的窗口组件的操作。例如, M o t i f Wi d g e t F a c t o r y的C r e a t e S c r o l l B a r操作实例化并返回一个M o t i f滚动条,而相应的P M Wi d g e t F a c t o r y操作返回一个Presentation Manager的滚动条。客户仅通过Wi d g e t F a c t o r y接口创建窗口组件,他们并不知道哪些类实现了特定视感风格的窗口组件。换言之,客户仅与抽象类定义的接口交互,而不使用特定的具体类的接口。
        Wi d g e t F a c t o r y也增强了具体窗口组件类之间依赖关系。一个M o t i f的滚动条应该与M o t i f按钮、M o t i f正文编辑器一起使用,这一约束条件作为使用M o t i f Wi d g e t F a c t o r y的结果被自动加上。

4. 适用性

       在以下情况可以使用Abstract Factory模式
       • 一个系统要独立于它的产品的创建、组合和表示时。
       • 一个系统要由多个产品系列中的一个来配置时。
       • 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
       • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

5. 结构

       此模式的结构如下图所示。

6. 参与者

        • A b s t r a c t F a c t o r y ( Wi d g e t F a c t o r y )
              — 声明一个创建抽象产品对象的操作接口。
        •C o n c r e t e F a c t o r y ( M o t i f Wi d g e t F a c t o r y,P M Wi d g e t F a c t o r y )
              — 实现创建具体产品对象的操作。
        • A b s t r a c t P r o d u c t ( Wi n d o w s,S c r o l l B a r )
              — 为一类产品对象声明一个接口。
        • C o n c r e t e P r o d u c t ( M o t i f Wi n d o w,M o t i f S c r o l l B a r )
              — 定义一个将被相应的具体工厂创建的产品对象。
              — 实现A b s t r a c t P r o d u c t接口。
        • C l i e n t
              — 仅使用由A b s t r a c t F a c t o r y和A b s t r a c t P r o d u c t类声明的接口。• A b s t r a c t F a c t o r y ( Wi d g e t F a c t o r y )

7. 协作

        • 通常在运行时刻创建一个C o n c r e t e F a c t r o y类的实例。这一具体的工厂创建具有特定实现
        的产品对象。为创建不同的产品对象,客户应使用不同的具体工厂。
        • AbstractFactory将产品对象的创建延迟到它的C o n c r e t e F a c t o r y子类。

8. 效果

        A b s t r a c t F a c t o r y模式有下面的一些优点和缺点:
        1) 它分离了具体的类Abstract Factory模式帮助你控制一个应用创建的对象的类。因为一个工厂封装创建产品对象的责任和过程,它将客户与类的实现分离。客户通过它们的抽象接口操纵实例。产品的类名也在具体工厂的实现中被分离;它们不出现在客户代码中。
        2) 它使得易于交换产品系列一个具体工厂类在一个应用中仅出现一次—即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。在我们的用户界面的例子中,我们仅需转换到相应的工厂对象并重新创建接口,就可实现从M o t i f窗口组件转换为Presentation Manager窗口组件。
        3) 它有利于产品的一致性当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要。而A b s t r a c t F a c t o r y很容易实现这一点。
        4) 难以支持新种类的产品难以扩展抽象工厂以生产新种类的产品。这是因为A b s t r a c t F a c t o r y接口确定了可以被创建的产品集合。支持新种类的产品就需要扩展该工厂接口,这将涉及A b s t r a c t F a c t o r y类及其所有子类的改变。我们会在实现一节讨论这个问题的一个解决办法。

9. 实现

        下面是实现Abstract Factor模式的一些有用技术:
        1) 将工厂作为单件一个应用中一般每个产品系列只需一个C o n c r e t e F a c t o r y的实例。因此工厂通常最好实现为一个S i n g l e t o n(3 . 5)。
        2) 创建产品A b s t r a c t F a c t o r y仅声明一个创建产品的接口,真正创建产品是由C o n c r e t e P r o d u c t子类实现的。最通常的一个办法是为每一个产品定义一个工厂方法(参见Factory Method(3 . 3))。一个具体的工厂将为每个产品重定义该工厂方法以指定产品。虽然这样的实现很简单,但它却要求每个产品系列都要有一个新的具体工厂子类,即使这些产品系列的差别很小。

        如果有多个可能的产品系列,具体工厂也可以使用P r o t o t y p e(3 . 4)模式来实现。具体工厂使用产品系列中每一个产品的原型实例来初始化,且它通过复制它的原型来创建新的产品。在基于原型的方法中,使得不是每个新的产品系列都需要一个新的具体工厂类。
        此处是S m a l l t a l k中实现一个基于原型的工厂的方法。具体工厂在一个被称为p a r t C a t a l o g的字典中存储将被复制的原型。方法m a k e:检索该原型并复制它:

make : partName
^ (partCatalog at : partName) copy
        具体工厂有一个方法用来向该目录中增加部件。
addPart : partTemplate named : partName
partCatalog at : partName put : partTemplate
        原型通过用一个符号标识它们,从而被增加到工厂中:
aFactory addPart : aPrototype named : #ACMEWidget
        在将类作为第一类对象的语言中(例如S m a l l t a l k和O b j e c t i v e C),这个基于原型的方法可能有所变化。你可以将这些语言中的一个类看成是一个退化的工厂,它仅创建一种产品。你可以将类存储在一个具体工厂中,这个具体工厂在变量中创建多个具体的产品,这很像原型。这些类代替具体工厂创建了新的实例。你可以通过使用产品的类而不是子类初始化一个具体工厂的实例,来定义一个新的工厂。这一方法利用了语言的特点,而纯基于原型的方法是与
语言无关的。
        像刚讨论过的S m a l l t a l k中的基于原型的工厂一样,基于类的版本将有一个唯一的实例变量p a r t C a t a l o g,它是一个字典,它的主键是各部分的名字。p a r t C a t a l o g存储产品的类而不是存储被复制的原型。方法m a k e:现在是这样:
make : partName

^ (partCatalog at : partName) new

        3) 定义可扩展的工厂A b s t r a c t F a c t o r y通常为每一种它可以生产的产品定义一个操作。产品的种类被编码在操作型构中。增加一种新的产品要求改变A b s t r a c t F a c t o r y的接口以及所有与它相关的类。一个更灵活但不太安全的设计是给创建对象的操作增加一个参数。该参数指定了将被创建的对象的种类。它可以是一个类标识符、一个整数、一个字符串,或其他任何可以标识这种产品的东西。实际上使用这种方法, A b s t r a c t F a c t o r y只需要一个“ M a k e”操作和一个指示要创建对象的种类的参数。这是前面已经讨论过的基于原型的和基于类的抽象工厂的技术。
        C + +这样的静态类型语言与相比,这一变化更容易用在类似于S m a l l t a l k这样的动态类型语言中。仅当所有对象都有相同的抽象基类,或者当产品对象可以被请求它们的客户安全的强制转换成正确类型时,你才能够在C + +中使用它。Factory Method(3.3)的实现部分说明了怎样在C + +中实现这样的参数化操作。
        该方法即使不需要类型强制转换,但仍有一个本质的问题:所有的产品将返回类型所给定的相同的抽象接口返回给客户。客户将不能区分或对一个产品的类别进行安全的假定。如果一个客户需要进行与特定子类相关的操作,而这些操作却不能通过抽象接口得到。虽然客户可以实施一个向下类型转换( d o w n c a s t)(例如在C + +中用d y n a m i c c a s t),但这并不总是可行或安全的,因为向下类型转换可能会失败。这是一个典型的高度灵活和可扩展接口的权衡折衷。

10. 代码示例

        我们将使用Abstract Factory模式创建我们在这章开始所讨论的迷宫。

        类M a z e F a c t o r y可以创建迷宫的组件。它建造房间、墙壁和房间之间的门。它可以用于一个从文件中读取迷宫说明图并建造相应迷宫的程序。或者它可以被用于一个随机建造迷宫的程序。建造迷宫的程序将M a z e F a c t o r y作为一个参数,这样程序员就能指定要创建的房间、墙壁和门等类。

        回想一下建立一个由两个房间和它们之间的门组成的小迷宫的成员函数C r e a t e M a z e。C r e a t e M a z e对类名进行硬编码,这使得很难用不同的组件创建迷宫。
        这里是一个以M a z e F a c t o r y为参数的新版本的CreateMaze ,它修改了以上缺点:

        我们创建M a z e F a c t o r y的子类E n c h a n t e d M a z e F a c t o r y,这是一个创建施了魔法的迷宫的工厂。E n c h a n t e d M a z e F a c t o r y将重定义不同的成员函数并返回R o o m,Wa l l等不同的子类。

        现在假设我们想生成一个迷宫游戏,在这个游戏里,每个房间中可以有一个炸弹。如果这个炸弹爆炸,它将(至少)毁坏墙壁。我们可以生成一个R o o m的子类以明了是否有一个炸弹在房间中以及该炸弹是否爆炸了。我们也将需要一个Wa l l的子类以明了对墙壁的损坏。我们将称这些类为R o o m Wi t h A B o m b和B o m b e d Wa l l。
        我们将定义的最后一个类是B o m b e d M a z e F a c t o r y,它是M a z e F a c t o r y的子类,保证了墙壁
是B o m b e d Wa l l类的而房间是R o o m Wi t h A B o m b的。B o m b e d M a z e F a c t o r y仅需重定义两个函数:

        为建造一个包含炸弹的简单迷宫,我们仅用B o m b e d M a z e F a c t o r y调用C r e a t e M a z e。

MazeGame game;
BombedMazeFactory factory;
g a m e . C r e a t e M a z e ( f a c t o r y ) ;
        C r e a t e M a z e也可以接收一个E n c h a n t e d M a z e F a c t o r y实例来建造施了魔法的迷宫。
        注意M a z e F a c t o r y仅是工厂方法的一个集合。这是最通常的实现Abstract Factory模式的方式。同时注意M a z e F a c t o r y 不是一个抽象类;因此它既作为A b s t r a c t F a c t o r y 也作为C o n c r e t e F a c t o r y。这是Abstract Factory 模式的简单应用的另一个通常的实现。因为M a z e F a c t o r y是一个完全由工厂方法组成的具体类,通过生成一个子类并重定义需要改变的操作,它很容易生成一个新的M a z e F a c t o r y。
        C r e a t e M a z e使用房间的S e t S i d e操作以指定它们的各面。如果它用一个B o m b e d M a z e F a c t o r y创建房间,那么该迷宫将由有B o m b e d Wa l l 面的R o o m Wi t h A B o m b 对象组成。如果R o o m Wi t h A B o m b必须访问一个B o m b e d Wa l l的与特定子类相关的成员,那么它将不得不对它的墙壁引用以进行从Wa l l *到B o m b e d Wa l l *的转换。只要该参数确实是一个B o m b e d Wa l l,这个向下类型转换就是安全的,而如果墙壁仅由一个B o m b e d M a z e F a c t o r y创建就可以保证这一点。
        当然,像S m a l l t a l k这样的动态类型语言不需要向下类型转换,但如果它们在应该是Wa l l的子类的地方遇到一个Wa l l类可能会产生运行时刻错误。使用Abstract Factory建造墙壁,通过确定仅有特定类型的墙壁可以被创建,从而有助于防止这些运行时刻错误。

        让我们考虑一个S m a l l t a l k版本的M a z e F a c t o r y,它仅有一个以要生成的对象种类为参数的m a k e操作。此外,具体工厂存储它所创建的产品的类。
        首先,我们用S m a l l t a l k写一个等价的C r e a t e M a z e:

        正如我们在实现一节所讨论, M a z e F a c t o r y仅需一个实例变量p a r t C a t a l o g来提供一个字典,这个字典的主键为迷宫组件的类。也回想一下我们是如何实现make:方法的:

make: partName

^ (partCatalog at partName) new

        现在我们可以创建一个M a z e F a c t o r y并用它来实现C r e a t e M a z e。我们将用类M a z e G a m e的一个方法C r e a t e M a z e F a c t o r y来创建该工厂。

        通过将不同的类与它们的主键相关联,就可以创建一个B o m b e d M a z e F a c t o r y或E n c h a n t e d M a z e F a c t o r y。例如,一个E n c h a n t e d M a z e F a c t o r y可以这样被创建:

11. 已知应用

        I n t e r Vi e w使用“K i t”后缀[ L i n 9 2 ]来表示A b s t r a c t F a c t o r y类。它定义Wi d g e t K i t和D i a l o g K i t抽象工厂来生成与特定视感风格相关的用户界面对象。I n t e r Vi e w还包括一个L a y o u t K i t,它根据所需要的布局生成不同的组成( c o m p o s i t i o n)对象。例如,一个概念上是水平的布局根据文档的定位(画像或是风景)可能需要不同的组成对象。
        E T + + [ W G M 8 8 ]使用Abstract Factory模式以达到在不同窗口系统(例如, X Wi n d o w s和S u n Vi e w)间的可移植性。Wi n d o w S y s t e m抽象基类定义一些接口,来创建表示窗口系统资源的对象(例如M a k e Wi n d o w、M a k e F o n t、M a k e C o l o r)。具体的子类为某个特定的窗口系统实现这些接口。运行时刻, E T + +创建一个具体Wi n d o w S y s t e m子类的实例,以创建具体的系统资源对象。

12. 相关模式

        A b s t r a c t F a c t o r y类通常用工厂方法( Factory Method (3 . 3))实现,但它们也可以用P r o t o t y p e实现。
        一个具体的工厂通常是一个单件( S i n g l e t o n(3 . 5))

本文出自《设计模式-可复用面向对象软件的基础》

 

 

 

转载请注明出处!!!

http://blog.csdn.net/stuqbx