《设计模式可复用面向对象软件的基础03》由会员分享,可在线阅读,更多相关《设计模式可复用面向对象软件的基础03(37页珍藏版)》请在金锄头文库上搜索。
1、下载 第3章创建型模式 创建型模式抽象了实例化过程。它们帮助一个系统独立于如何创建、组合和表示它的那 些对象。一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委 托给另一个对象。 随着系统演化得越来越依赖于对象复合而不是类继承,创建型模式变得更为重要。当这 种情况发生时,重心从对一组固定行为的硬编码( h a r d - c o d i n g)转移为定义一个较小的基本 行为集,这些行为可以被组合成任意数目的更复杂的行为。这样创建有特定行为的对象要求 的不仅仅是实例化一个类。 在这些模式中有两个不断出现的主旋律。第一,它们都将关于该系统使用哪些具体的类 的信息封装起来。第
2、二,它们隐藏了这些类的实例是如何被创建和放在一起的。整个系统关 于这些对象所知道的是由抽象类所定义的接口。因此,创建型模式在什么被创建,谁创建它, 它是怎样被创建的,以及何时创建这些方面给予你很大的灵活性。它们允许你用结构和功能 差别很大的“产品”对象配置一个系统。配置可以是静态的(即在编译时指定) ,也可以是动 态的(在运行时) 。 有时创建型模式是相互竞争的。例如,在有些情况下 P r o t o t y p e(3 . 4)或Abstract Factory (3 . 1)用起来都很好。而在另外一些情况下它们是互补的: B u i l d e r(3 . 2)可以使用其他模式 去实现某个
3、构件的创建。P r o t o t y p e(3 . 4)可以在它的实现中使用S i n g l e t o n(3 . 5) 。 因为创建型模式紧密相关,我们将所有5个模式一起研究以突出它们的相似点和相异点。 我们也将举一个通用的例子为一个电脑游戏创建一个迷宫来说明它们的实现。这个迷 宫和游戏将随着各种模式不同而略有区别。有时这个游戏将仅仅是找到一个迷宫的出口;在 这种情况下,游戏者可能仅能见到该迷宫的局部。有时迷宫包括一些要解决的问题和要战胜 的危险,并且这些游戏可能会提供已经被探索过的那部分迷宫地图。 我们将忽略许多迷宫中的细节以及一个迷宫游戏中有一个还是多个游戏者。我们仅关注 迷宫是
4、怎样被创建的。我们将一个迷宫定义为一系列房间,一个房间知道它的邻居;可能的 邻居要么是另一个房间,要么是一堵墙、或者是到另一个房间的一扇门。 类R o o m、D o o r和Wa l l定义了我们所有的例子中使用到的构件。我们仅定义这些类中对创 建一个迷宫起重要作用的一些部分。我们将忽略游戏者、显示操作和在迷宫中四处移动操作, 以及其他一些重要的却与创建迷宫无关的功能。 下页图表示了这些类之间的关系。 每一个房间有四面,我们使用C + +中的枚举类型D i r e c t i o n来指定房间的东南西北: enum Direction North, South, East, West; S
5、m a l l t a l k的实现使用相应的符号来表示这些方向。 类M a p S i t e是所有迷宫组件的公共抽象类。为简化例子, M a p S i t e仅定义了一个操作E n t e r, 它的含义决定于你在进入什么。如果你进入一个房间,那么你的位置会发生改变。如果你试 图进入一扇门,那么这两件事中就有一件会发生:如果门是开着的,你进入另一个房间。如 果门是关着的,那么你就会碰壁。 E n t e r为更加复杂的游戏操作提供了一个简单基础。例如,如果你在一个房间中说“向东 走” ,游戏只能确定直接在东边的是哪一个 M a p S i t e并对它调用E n t e r。特定子类的E
6、 n t e r操作将 计算出你的位置是发生改变,还是你会碰壁。在一个真正的游戏中, E n t e r可以将移动的游戏 者对象作为一个参数。 R o o m是M a p S i t e的一个具体的子类,而 M a p S i t e定义了迷宫中构件之间的主要关系。 R o o m有指向其他M a p S i t e对象的引用,并保存一个房间号,这个数字用来标识迷宫中的房间。 下面的类描述了一个房间的每一面所出现的墙壁或门。 第3章创建型模式5 5 下载 我们不仅需要知道迷宫的各部分,还要定义一个用来表示房间集合的M a z e类。用 R o o m N o操作和给定的房间号,M a z e就
7、可以找到一个特定的房间。 R o o m N o可以使用线形搜索、h a s h表、甚至一个简单数组进行一次查找。但我们在此处并 不考虑这些细节,而是将注意力集中于如何指定一个迷宫对象的构件上。 我们定义的另一个类是M a z e G a m e,由它来创建迷宫。一个简单直接的创建迷宫的方法是 使用一系列操作将构件增加到迷宫中,然后连接它们。例如,下面的成员函数将创建一个迷 宫,这个迷宫由两个房间和它们之间的一扇门组成: 考虑到这个函数所做的仅是创建一个有两个房间的迷宫,它是相当复杂的。显然有办法使它 变得更简单。例如,R o o m的构造器可以提前用墙壁来初始化房间的每一面。但这仅仅是将代
8、码移到了其他地方。这个成员函数真正的问题不在于它的大小而在于它不灵活。它对迷宫的 布局进行硬编码。改变布局意味着改变这个成员函数,或是重定义它这意味着重新实现 整个过程或是对它的部分进行改变这容易产生错误并且不利于重用。 创建型模式显示如何使得这个设计更灵活,但未必会更小。特别是,它们将便于修改定 5 6设计模式:可复用面向对象软件的基础 下载 义一个迷宫构件的类。 假设你想在一个包含(所有的东西)施了魔法的迷宫的新游戏中重用一个已有的迷宫布 局。施了魔法的迷宫游戏有新的构件,像 D o o r N e e d i n g S p e l l,它是一扇仅随着一个咒语才能被 锁上和打开的门;以及
9、 E n c h a n t e d R o o m,一个可以有不寻常东西的房间,比如魔法钥匙或是 咒语。你怎样才能较容易的改变C r e a t e M a z e以让它用这些新类型的对象创建迷宫呢? 这种情况下,改变的最大障碍是对被实例化的类进行硬编码。创建型模式提供了多种不 同方法从实例化它们的代码中除去对这些具体类的显式引用: 如果C r e a t e M a z e调用虚函数而不是构造器来创建它需要的房间、墙壁和门,那么你可以 创建一个M a z e G a m e的子类并重定义这些虚函数,从而改变被例化的类。这一方法是 Factory Method(3 . 3)模式的一个例子。
10、如果传递一个对象给C r e a t e M a z e作参数来创建房间、墙壁和门,那么你可以传递不同的 参数来改变房间、墙壁和门的类。这是 Abstract Factory(3 . 1)模式的一个例子。 如果传递一个对象给C r e a t e M a z e,这个对象可以在它所建造的迷宫中使用增加房间、墙 壁和门的操作,来全面创建一个新的迷宫,那么你可以使用继承来改变迷宫的一些部分 或该迷宫被建造的方式。这是B u i l d e r(3 . 2)模式的一个例子。 如果C r e a t e M a z e由多种原型的房间、墙壁和门对象参数化,它拷贝并将这些对象增加到 迷宫中,那么你可以用
11、不同的对象替换这些原型对象以改变迷宫的构成。这是 P r o t o t y p e (3 . 4)模式的一个例子。 剩下的创建型模式,S i n g l e t o n(3 . 5) ,可以保证每个游戏中仅有一个迷宫而且所有的游戏 对象都可以迅速访问它不需要求助于全局变量或函数。S i n g l e t o n也使得迷宫易于扩展或替 换,且不需变动已有的代码。 3.1 ABSTRACT FACTORY(抽象工厂)对象创建型模式 1. 意 图 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 2. 别 名 K i t 3. 动 机 考虑一个支持多种视感(l o o k -
12、 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类,这个类声明了一个用来创建 每一类基本窗口组件的接口。每一类窗口组件都有一个抽象类,而具体子类则实现了窗口组 件的特定视感风格。对于每一个抽象
13、窗口组件类, Wi d g e t F a c t o r y接口都有一个返回新窗口组 件对象的操作。客户调用这些操作以获得窗口组件实例,但客户并不知道他们正在使用的是 哪些具体类。这样客户就不依赖于一般的视感风格,如下页图所示。 第3章创建型模式5 7 下载 每一种视感标准都对应于一个具体的 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
14、 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的结果被自动
15、加 上。 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 ) 5 8设计模式:可复用面向对象软件的基础 下载 声明一个创建抽象产品对象的操作接口。 C o n c r e t e F a c t o r
16、 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类声明的接口。 7. 协 作 通常在运行时刻创建一个C o n c r e t e F a c t r o y类的实例。这一具体的工厂创建