先来看一个《设计模式》中的例子,这段代码很好的简单描述了一个迷宫的设计和实现。只要在加点函数定义它也就可以工作了。但是它很不灵活。它对迷宫的布局进行硬编码。并且它也对迷宫的构件进行了硬编码。
创建型模式显示如何使得这个设计更灵活,但未必更小。特别是,它们将便于修改定义一个迷宫构件的类。
假设你想在一个包含(所有的东西)施了魔法的迷宫的新游戏中
重用一个已有的迷宫布局。施了魔法的迷宫游戏有新的构件,像DoorNeedingSpell,它是一扇仅随着一个咒语才能被锁上和打开的门。你怎样才能较容易的改变CreateMaze以让它用这些新类型的对象创建迷宫呢?
这种情况下,改变的最大障碍是对被实例化的类进行了硬编码。创建型模式提供了多种不同的方法从实例化它们的代码中除去对这些具体类的显示引用:
- 如果CreateMaze调用虚函数而不是构造器来创建它需要的房间、墙壁和门,那么你可以创建一个MazeGame的子类并重定义这些虚函数,从而改变被实例化的类。这个方法是Factory Method模式的一个例子。
- 如果传递一个对象给CreateMaze作参数来创建房间、墙壁和门,那么你可以传递不同的参数来改变房间、墙壁和门的类。这是Abstract Factory模式的一个例子。
- 如果传递一个对象给CreateMaze,这个对象可以在它所创建的迷宫中使用增加房间、墙壁和门的操作,来全面创建一个新的迷宫,那么你可以使用继承来改变迷宫的一些部分或该迷宫被创建的方式。这是Builder模式的一个例子。
- 如果CreateMaze由多种原型的房间、墙壁和门对象初始化,它拷贝并将这些对象增加到迷宫中,那么你可以用不同的对象替换这些原型对象以改变迷宫的构成。这是Prototype模式的一个例子。
剩下的创建型模式,Singleton模式可以保证每个游戏中仅有一个迷宫而且所有的游戏对象都可以迅速访问它。
class MapSite {
public:
virtual void Enter() = 0;
};
class Room : public MapSite {
public:
Room(int roomNo);
MapSite* GetSide(Direction) const;
void SetSide(Direction, MapSite*);
virtual void Enter();
private:
MapSite* _sides[4];
int _roomNumber;
};
class Wall : public MapSite {
public:
Wall();
virtual void Enter();
}
class Door : public MapSite {
public:
Door(Room * = 0, Room * = 0);
virtual void Enter();
Room OtherSideFrom(Room *);
private:
Room* _room1;
Room* _room2;
bool _isOpen;
};
class Maze {
public:
Maze();
void AddRoom(Room*);
Room* RoomNo(int) const;
private:
//
};
class MazeGame {
public:
MazeGame();
Maze* CreateMaze();
};
Maze* MazeGame::CreateMaze() {
Maze* aMaze = new Maze();
Room* r1 = new Room(1);
Room* r2 = new Room(2);
Door* theDoor = new Door(r1, r2);
aMaze->AddRoom(r1);
aMaze->AddRoom(r2);
r1->SetSide(North, new Wall());
r1->SetSide(East, theDoor);
r1->SetSide(South, new Wall());
r1->SetSide(West, new Wall());
r2->SetSide(North, new Wall());
r2->SetSide(East, new Wall());
r2->SetSide(South, new Wall());
r2->SetSide(West, theDoor);
return aMaze;
}
Abstract Factory(抽象工厂)
意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用性
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
结构
效果
- 它分离了具体的类 Abstract Factory模式帮助你控制一个应用创建的对象的类。它将客户与类的实现分离。客户通过它们的抽象接口操纵实例。产品的类名也在具体工厂的实现中被分离;它们不出现在客户代码中。
- 它使得易于交换产品系列
- 它有利于产品的一致性 当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要。而Abstract Factory很容易实现这一点。
- 难以支持新种类的产品
Builder(生成器)
意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
结构
效果
- 它使你可以改变一个产品的内部表示 Builder对象提供给导向器一个构造产品的抽象接口。因为产品是通过抽象接口构造的,你在改变该产品的内部表示时所要做的只是定义一个新的生成器。
- 它将构造代码和表示代码分开 Builder模式通过封装一个复杂对象的创建和表示方法提高了对象的模块性。
- 它使你可对构造过程进行更精细的控制 Builder模块与一下子就生成产品的创建型模式不同,它是在导向者的控制下一步一步构造产品的。
Factory Method(工厂方法)
意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
适用性
- 当一个类不知道它所必须创建的对象的类的时候
- 当一个类希望由它的子类来创建具体类的时候
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类作为代理者这一信息局部化的时候
结构
效果
- 工厂方法不再将与特定应用有关的类绑定到你的代码中。代码仅处理Product接口;因此它可以与用户定义的任何ConcreteProduct类一起使用。
- 工厂方法的一个潜在缺点在于客户可能仅仅为了创建一个特定的ConcreteProduct对象,就不得不创建Creator的子类。
- 为子类提供挂钩(hook) Factory Method给子类一个挂钩以提供对象的扩展版本。
Prototype(原型)
意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用性
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载
- 为了避免创建一个与产品类层次平行的工厂类层次时
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
结构
效果
- 运行时刻增加和删除产品
- 改变值以指定新对象
- 改变结构以指定新对象
- 减少子类的构造
- 用类动态配置应用
Singleton(单件)
意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
- 当这个唯一实例应该是通过子类化可扩展的,并且俄客户应该无需更改代码就能使用一个扩展的实例时。
结构
效果
- 对唯一实例的受控访问 因为Singleton类封装它的唯一实例,所以它可以严格的控制客户怎样以及合适访问它。
- 缩小名空间 Singleton模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染名空间。
- 允许对操作和表示的精化 Singleton类可以有子类,而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。
- 允许可变数目的实例 这个模式使得你易于改变你的想法,并允许Singleton类的多个实例。此外,你可以用相同的方法来控制应用所使用的实例的数目。只是允许访问Singleton实例的操作需要改变。
- 比类操作更灵活 另一种封装单件功能的方式是使用类操作(C++中静态成员函数)。但是C++中的静态成员函数不是虚函数,因此子类不能多态的重定义它们。