总览
设计模式原则:
开闭原则
对扩展开放,对修改关闭。
单一职责原则
一个类只负责一个功能领域中的相应职责。
里氏替换原则
所有使用基类的地方必须能透明地使用其子类对象。
依赖倒置原则
面向抽象编程,而不要面向具体对象编程。
接口隔离原则
类之间的依赖关系应该建立在最小的接口上。
迪米特法则
一个实体应尽可能少得与其他实体发生相互作用。
合成复用原则
尽可能使用合成,而不是继承来达到复用的目的。
设计模式
可变的方面
Abstract Factory(抽象工厂)
产品对象家族
Builder(生成器)
如何创建一个组合对象
Factory Method(工厂方法)
被实例化的子类
Prototype(原型)
被实例化的类
Singleton(单例模式)
一个类的唯一实例
Adapter(适配器)
对象的接口
Bridge(桥接)
对象的实现
Composite(组合)
一个对象的结构和组成
Decorator(装饰)
对象的职责,不生成子类
Facade(外观)
一个子系统的接口
Flyweight(享元)
对象的存储开销
Proxy(代理)
如何访问一个对象;该对象的位置
Chain of Responsibility(职责链)
满足一个请求的对象
Command(命令)
何时、怎样满足一个请求
Interpreter(解释器)
一个语言的文法及解释
Iterator(迭代器)
如何遍历、访问一个聚合的各元素
Mediator(中介者)
对象间如何交互、和谁交互
Memento(备忘录)
一个对象中哪些私有信息存放在该对象之外,以及在什么时候进行存储
Observer(观察者)
多个对象依赖于另外一个对象,而这些对象又如何保持一致
State(状态)
对象的状态
Strategy(策略)
算法
Template Method(模板方法)
算法中的某些步骤
Visitor(访问者)
某些可作用于一个对象(组)对象上的操作,但不修改这些对象的类
创建型模式
创建型设计模式抽象了实例化过程。
两个特征:第一,将使用哪些具体的类的信息封装起来。第二,隐藏了这些类的实例是如何被创建和放在一起的。
因此,创建型模式在==什么==被创建、==谁==创建了它、它是==怎样==被创建的,以及==何时==创建等方面给予了很大的灵活性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 enum Direction {North, South, East, West};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* = nullptr , Room* = nullptr ); 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 : }; 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(抽象工厂)
意图:
提供一个接口以创建一系列相关或相互依赖的对象,而无须指定它们具体的类。
适用性
一个系统要独立于它的产品的创建、组合和表示。
一个系统要由多个产品系列中的一个来配置。
要强调一系列相关的产品对象的设计以便进行联合使用。
提供一个产品类库,但只想显示它们的接口而不是实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class MazeFactory {public : MazeFactory (); virtual Maze* MakeMaze () const { return new Maze; } virtual Wall* MakeWall () const { return new Wall; } virtual Room* MakeRoom (int n) const { return new Room (n); } virtual Door* MakeDoor (Room* r1, Room* r2) const { return new Door (r1, r2); } }; Maze* Mazegame::CreatMaze (MazeFactory& factory) { Maze* aMaze = factoty.MakeMaze (); Room* r1 = factory.MakeRoom (1 ); Room* r2 = factory.MakeRoom (2 ); Door* aDoor = factory.MakeDoor (r1, r2); aMaze->AddRoom (r1); aMaze->AddRoom (r2); r1->SetSize (North, factory.MakeWall ()); r1->SetSide (East, aDoor); r1->SetSize (South, factory.MakeWall ()); r1->SetSize (West, factory.MakeWall ()); r2->SetSize (North, factory.MakeWall ()); r2->SetSize (East, factory.MakeWall ()); r2->SetSize (South, factory.MakeWall ()); r2->setSide (West, aDoor); return aMaze; }; class EnchantedMazeFactory : public MazeFactory {public : EnchantedMazeFactory (); virtual Room* MakeRoom (int n) override const { return new EnchantedRoom (n, CastSpell ()); } virtual Door* MakeRoom (int n) override const { return new DoorNeedingSpell (r1, r2); } private : Spell* CastSpell () const ; }; int main () { Mazegame game; EnchantedMazeFactory factroy; game.CreateMaze (factory); NewFactory nfactory; game.CreateMaze (nfactory); return 0 ; }
协作:
通常在运行时创建一个具体工厂类的实例。这一具体的工厂创建具有特定实现的产品对象。为创建不同的产品对象,需要使用不同的具体工厂。
抽象工厂类将产品对象的创建延迟到了它的具体工厂子类。
效果:
优点
分离了具体的类。
使得易于交换产品系列。
有利于产品的一致性。
缺点
难以支持新种类的产品:能够创建的产品在抽象类中都定义好了接口,如果扩展接口就势必要修改抽象类。可以通过Prototype模式来解决。
Builder(生成器)
意图
将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须允许被构造对象有不同的表达时。
协作
客户创建Director对象,并用它所想要的Builder对象进行配置。
一旦生成了产品部件,导向器就会通知生成器。
生成器处理导向器的请求,并将部件添加到该产品中。
客户从生成器中检索产品。
效果
它使你可以改变一个产品的内部展示。
他将构造代码和表示代码分开。
它使你可对构造过程进行更精细的控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 class MazeBuilder {public : virtual void BuildMaze () {} virtual void BuildRoom (int n) {} virtual void BuildDoor (int roomFrom, int roonTo) {} virtual Maze* GetMaze () {return 0 ;} protected : MazeBuilder (); }; Maze* MazeGame::CreateMaze (MazeBuilder& builder) { builder.BuildMaze (); builder.BuildRoom (1 ); builder.BuildRoom (2 ); builder.BuildDoor (1 , 2 ); return builder.GetMaze (); } class StandardMazeBuilder : public MazeBuilder {public : StandardMazeBuilder (); virtual void BuildMaze () override ; virtual void BuildRoom (int ) override ; virtual void BuildDoor (int , int ) override ; virtual Maze* GetMaze () override ; private : Dirction CommonWall (Room*, Room*) ; Maze* _currentMaze; }; StandardMazeBuilder::StandarMazeBuilder () { _currentMaze = nullptr ; } void StandardMazeBuilder::BuildMaze () { _currentMaze = new Maze; } void StandardMazeBuilder::BuildRoom (int n) { if (!_currentMaze->RoomNo (n)) { Room* room = new Room (n); _currentMaze->AddRoom (room); room->SetSide (North, new Wall); room->SetSide (East, new Wall); room->SetSide (South, new Wall); room->SetSide (West, new Wall); } } void StandardMazeBuilder::BuildDoor (int n1, int n2) { Room* r1 = _currentMaze->RoomNo (n1); Room* r2 = _currentMaze->RoomNo (n2); Door* d = new Door (r1, r2); r1->SetSide (CommonWall (r1, r2), d); r2->SetSide (CommonWall (r2, r1), d); } int main () { Maze* maze; MazeGame game; StandardMazeBuilder builder; game.CreateMaze (builder); maze = builder.GetMaze (); }
相关模式
Abstract Factory和Builder模式看起来是非常相似的,都可以用来创建复杂的对象。区别之处在于,Builder模式着重于一步一步构建一个复杂对象,而Abstract Factory着重于多个系列的产品对象。Builder在最后一步返回产品,而Abstract Factory的产品是立即返回的。
Composite模式通常是用Builder生成的。
Factory Method(工厂方法)
意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
适用性
当一个类不知道它所必须创建的对象的类的时候。
当一个类希望由它的子类来指定它所创建的对象的时候。
当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
协作
Creator依赖于它的子类来定义工厂方法,所以它返回一个适当的ConcreteProduct实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class MazeGame {public : Maze* CreateMaze () ; virtual Maze* MakeMaze () const { return new Maze; } virtual Room* MakeRoom (int n) const { return new Room (n); } virtual Wall* MakeWall () const { return new Wall; } virtual Door* MakeDoor (Room* r1, Room* r2) const { return new Door (r1, r2); } }; Maze* MazeGame::CreateMaze () { Maze* aMaze = MakeMaze (); Room* r1 = MakeRoom (1 ); Room* r2 = MakeRoom (2 ); Door* theDoor = MakeDoor (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; } class EnchantedMazeGame : public MazeGame {public : EnchantedMazeGame (); virtual Room* MakeRoom (int n) const { return new EnchantedRoom (n, CastSpell ()); } virtual Door* MakeDoor (Room* r1, Room* r2) const { return new DoorNeedingSpell (r1, r2); } protected : Spell* CastSpell () const ; };
工厂方法可以理解为不要将对象的各种一大坨实现都放在构造函数中,而是要拆分成多个部分的函数来组合实现。
相关模式
Abstract Factory经常用工厂方法来实现。
工厂方法通常在Template Method中被调用。
Prototype不需要创建Creator的子类。但是它们通常要求一个针对Product类的Initialize操作。Creator使用Initialize来初始化对象,而Factory Method不需要这样 的操作。
Prototype(原型)
意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用性 ‘
当一个系统应该独立于它的产品创建、构成和表示时。
当要实例化的类是在运行时指定时,例如,通过动态装载。
为了避免创建一个与产品类层次平行的工厂类层次时。
当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
效果
运行时增加和删除产品。
改变值以指定新对象。
改变结构以指定新对象。
减少子类的构造。
用类动态配置应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class MazePrototypeFactory : public MazeFactory {public : MazePrototypeFactory (Maze*, Wall*, Room*, Door*); virtual Maze* MakeMaze () override const ; virtual Room* MakeRoom (int ) override const ; virtual Wall* MakeWall () override const ; virtual Door* MakeDoor (Room*, Room*) override const ; private : Maze* _prototypeMaze; Room* _prototypeRoom; Wall* _prototypeWall; Door* _prototypeDoor; }; MazePrototypeFactory::MazePrototypeFactory (Maze* m, Wall* w, Room* r, Door* d) : _prototypeMaze(m), _prototypeRoom(r), _prototypeWall(w), _prototypeDoor(d) {} Wall* MazePrototypeFactory::MakeWall () override const { return _prototypeWall->Clone (); } Door* MazePrototypeFactory::MakeDoor (Room* r1, Room* r2) override const { Door* door = _prototypeDoor->Clone (); door->Initialize (r1, r2); return door; } int main () { MazeGame game; MazePrototypeFactory simpleMazeFactory (new Maze, new Wall, new Room, new Door) ; Maze* maze = game.CreateMaze (simpleMazeFactory); }
Singleton(单例模式)
意图
保证一个类仅有一个实例。并提供一个访问它的全局访问点。
适用性
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
实现
将创建唯一实例的操作隐藏在一个类操作(即一个静态成员函数或者一个类方法)后面,由它来保证只有一个实例被创建。
在C++中可以用Singleton类的静态成员函数Instance来定义这个类操作。Singleton还定义了一个静态成员变量_instance,它包含了一个指向它的唯一实例的指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class MazeFactory {public : static MazeFactory* Instance () ; protected : MazeFactory (); private : static MazeFactory* _instance; }; MazeFactory* MazeFactory::_instance = nullptr ; MazeFactory* MazeFactory::Instance () { if (_instance == nullptr ) { _instance = new MazeFactory; } return _instance; } MazeFactory* MazeFactory::Instance () { if (_instance == nullptr ) { const char * mazeStyle = getenv ("MAZESTYLE" ); if (strcmp (mazestyle, "bombed" ) == 0 ) { _instance = new BombedMazeFactory; } else if (strcmp (mazestyle, "enchanted" ) == 0 ) { _instance = new EnchantedMazeFactory; } else { _instance = new MazeFactory; } } return _instance; }
结构性模式
结构性模式涉及如何组合类和对象以获得更大的结构。结构类模式采用继承机制来组合接口或实现。
结构型对象模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能的一些方法。因为可以在运行时改变对象组合关系,所以对象组合方式具有更大的灵活性,而这种机制用静态类组合是不可能实现的。
Adapter(适配器)
意图
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用性
你想使用一个已经存在的类,而它的接口不符合你的需求。
你想创建一个已经复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
(仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
协作
Client在Adapter实例上调用一些操作,接着适配器调用Adaptee的操作实现这个请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 class Shape {public : Shape (); virtual void BoundingBox (Point& bottomLeft, Point& topRight) const ; virtual Manipulator* CreateManipulator () const ; }; class TextView {public : TextView (); void GetOrigin (Coord& x, Coord& y) const ; void GetExtent (Coord& width, Coord& height) const ; virtual bool IsEmpty () const ; }; class TextShape : public Shape, private TextView {public : TextShape (); virtual void BoundingBox (Point& bottomLeft, Point& topRight) const ; virtual bool IsEmpty () const ; virtual Manipulator* CreateManipulator () const ; }; void TextShape::BoundingBox (Point& bottomLeft, Point& tpRight) const { Coord bottom, left, width, height; GetOrigin (bottom, left); GetExtent (width, height); bottomLeft = Point (bottom, left); topRight = Point (bottom + height, left + width); } void TextShape::IsEmpty () const { return TextView::IsEmpty (); } Manipulator* TextShape::CreateManipulator () const { return new TextManipulator (this ); } class TextShape : public Shape {public : TextShape (TextView*); virtual void BoundingBox (Point& bottomLeft, Point& topRight) const ; virtual bool IsEmpty () const ; virtual Manipulator* CreateManipulator () const ; private : TextView* _text; }; TextShape::TextShape (TextView* t) { _text = t; } void TextShape::BoundingBox (Point& bottomLeft, Point& topRight) const { Coord Bottom, left, width, height; _text->GetOrigin (bottom, left); _text->GetExtent (width, height); bottomLeft = Point (bottom, left); topRight = Point (bottom + height, left + width); } bool TextShape::IsEmpty () const { return _text->IsEmpty (); } Manipulator* TextShape::CreateManipulator () const { return new TextManipulator (this ); }
相关模式
Bridge模式的结构与对象适配器类似,但是Bridge模式的出发点不同:Bridge模式的目的是将接口部分和实现部分分离,从而可以对它们较为容易也相对独立地加以改变。而Adaptor则意味着改变一个已有对象的接口。
Decorator模式增强了其他对象的功能而同时又不改变它的接口,因此Decorator对应用程序的透明性比适配器要好。结果是Decorator支持递归组合,而纯粹使用适配器是不可能实现这一点的。
Proxy模式在不改变它的接口的条件下,为另一个对象定义了一个代理。
Bridge(桥接)
意图
将抽象部分与它的实现部分分离,使它们可以独立地变化。
适用性
以下情况使用Bridge模式:
你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如,这种情况可能是因为,在程序运行时实现部分应可以被选择或者切换。
类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时Bridge模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
(C++)你想对客户完全隐藏抽象的实现部分。在C++中,类的表示在类接口中是可见的。
你想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。
效果
分离接口及其实现部分。
提高可扩充性。
实现细节对客户透明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 class Window {public : Window (View* contents); virtual void DrawContents () ; virtual void Open () ; virtual void Close () ; virtual void Iconify () ; virtual void Deiconify () ; virtual void SetOrigin (const Point& at) ; virtual void SetExtent (const Point& extent) ; virtual void Raise () ; virtual void Lower () ; virtual void DrawLine (const Point&, const Point&) ; virtual void DrawRect (const Point&, const Point&) ; virtual void DrawPolygon (const Point[], int n) ; virtual void DrawText (const char *, const Point&) ; protected : WindowImp* GetWindowImp () ; View* GetView () ; private : WindowImp* _imp; View* _contents; }; class WindowImp {public : virtual void ImpTop () = 0 ; virtual void Impbottom () = 0 ; virtual void ImpSetExtent (const Point&) = 0 ; virtual void ImpSetOrigin (const Point&) = 0 ; virtual void DeviceRect (Coord, Coord, Coord, Coord) = 0 ; virtual void DeviceText (const char *, Coord, Coord) = 0 ; virtual void DeviceBitmap (const char *, Coord, Coord) = 0 ; protected : WindowImp (); }; class ApplicationWindow : public Window {public : virtual void DrawContents () ; }; void ApplicationWindow::DrawContents () { GetView ()->DrawOn (this ); }; class IconWindow : public Window {public : virtual void DrawContents () ; private : const char * _bitmapName; }; void IconWindow::DrawContents () { WindowImp* imp = GetWindowImp (); if (imp != nullptr ) { imp->DeviceBitmap (_bitmapName, 0.0 , 0.0 ); } } void Window::DrawRect (const Point& p1, const Point& p2) { WindowImp* Imp = GetWindowImp (); imp->DeviceRect (p1.X (), p1.Y (), p2.X (), p2.Y ()); } class XWindowImp : public WindowImp {public : XWindowImp (); virtual void DeviceRect (Coord, Coord, Coord, Coord) ; private : Display* _dpy; Drawable _winid; GC _gc; }; void XWindowImp::DeviceRect (Coord x0, Coord y0, Coord x1, Coord y1) { int x = round (min (x0, x1)); int y = round (min (y0, y1)); int w = round (abs (x0 - x1)); int h = round (abs (y0 - y1)); XDrawRectangle (_dpy, _winid, _gc, x, y, w, h); } WindowImp* Window::GetWindowImp () { if (_imp == 0 ) { _imp = WindowSystemFactory::Instance ()->MakeWindowImp (); } return _imp; }
相关模式
Abstract Factory模式可以用来创建和配置一个特定的Bridge模式。
Adapter模式用来帮助无关的类协同工作,它通常在系统设计完成后才会被使用。然而,Bridge模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。
理解
Bridge模式是将一个复杂对象的功能和其实现分离开了,可能是因为功能在不同平台或环境有不同的实现形式。分离之后就不用指定固定的实现方式,可以根据不同情况选择不同的实现。
Composite(组合)
意图
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
适用性
你想表示对象的部分-整体层次结构。
你希望用户忽略组合对象和单个对象的不同,用户将统一地使用组合结构中的所有对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 class Equipment {public : virtual ~Equipment (); const char * Name () { return _name; } virtual Watt Power () ; virtual Currency NetPrice () ; virtual Currency DiscountPrice () ; virtual void Add (Quipment*) ; virtual void Remove (Equipment*) ; virtual Iterator<Equipment*>* CreateIterator () ; protected : Equipment (const char *); private : const char * _name; }; class FloppyDisk : public Equipment {public : FloppyDisk (const char *); virtual ~FloppyDisk (); virtual Watt Power () ; virtual Currency NetPrice () ; virtual Currency DiscountPrice () ; }; class CompositeEquipment : public Equipment {public : virtual ~CompositeEquipment (); virtual Watt Power () ; virtual Currency NetPrice () ; virtual Currency DiscountPrice () ; virtual void Add (Equipment*) ; virtual void Remove (Equipment*) ; virtual Iterator<Equipment*>* CreateIterator () ; protected : CompositeEquipment (const char *); private : List<Equipment*> _equipment; }; Currency CompositeEquipment::NetPrice () { Iterator<Equipment*>* i = CreateIterator (); Curency total = 0 ; for (i->First (); i->IsDone (); i->Next ()) { total += i->CurrentItem ()->NetPrice (); } delete i; return total; } class Chassis : public CompositeEquipment {public : Chassis (const char *); virtual ~Chassis (); virtual Watt Power () ; virtual Currency NetPrice () ; virtual Currency DiscountPrice () ; }; int main () { Cabinet* cabinet = new Cabinet ("PC Cabinet" ); Chassis* chassis = new Chassis ("PC Chassis" ); Bus* bus = new Bus ("MCA Bus" ); bus->Add (new Card ("16Mbs Token Ring" )); chassis->Add (bus); chassis->Add (new FloppyDisk ("3.5in FloppyDisk" )); cout << "The net price is " << chassis->NetPrice () << endl; }
相关模式
通常,部件-父类连接用于Responsibility of Chain模式。
Decorator模式经常与Composite模式一起使用。当装饰和组合一起使用时,它们通常有一个公共的父类。因此装饰必须支持具有Add、Remove和GetChild操作的Component接口。
FlyWeight让你共享组件,但不再能引用其父部件。
Iterator可用来遍历Composite。
Vistor将本来应该分布在Composite和Leaf类中的操作和行为局部化。
Decorator(装饰)
意图
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
适用性
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤销的职责。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,类定义被隐藏,或类定义不能用于生成子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class VisualComponent { public : VisualComponent (); virtual void Draw () ; virtual void Resize () ; }; class Decorator : public VisualComponet {public : Decorator (VisualComponent*); virtual void Draw () ; virtual void Resize () ; private : VisualComponent* _component; }; void Decorator::Draw () { _component->Draw (); } void Decorator::Resize () { _component->Resize (); } class BorderDecorator : public Decorator {public : BorderDecorator (VisualComponent*, int borderWidth); virtual void Draw () ; private : void DrawBorder (int ) ; private : int _width; }; void BorderDecorator::Draw () { Decorator::Draw (); DrawBorder (_width); } int main () { Window* window = new Window; TextView* TextView = new TextView; window->SetContents ( new BorderDecorator ( new ScrollDecorator (textView), 1 )); }
相关模式
Adapter:Decorator模式不同于Adapter模式,因为装饰仅改变对象的职责而不改变它的接口;而适配器将给对象一个全新的接口。
Composite:可以将装饰视为一个退化的、仅有一个组件的组合。然而,装饰仅给对象添加一些额外的职责——它的目的不在于对象聚集。
Strategy:用一个装饰可以改变对象的外表;而Strategy模式使得你可以改变对象的内核。这是改变对象的两种途径。
Facade(外观)
意图
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
适用性
当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂,大多数模式使用时都会产生更多更小的类。这使得子系统更具可复用性,也更容易对子系统进行定制,但也给那些不需要定制子系统的用户带来一些使用上的困难。Facade可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过Facade层。
客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
当你需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,可以让它们仅通过Facade进行通信,从而简化了它们之间的依赖关系。
协作
客户程序通过发送请求给Facade的方式与子系统通信,Facade将这些消息转发给适当的子系统对象。尽管是子系统中的有关对象在做实际工作,但Facade模式本身也必须将它的接口转换成子系统的接口。
使用Facade的客户程序不需要直接访问子系统对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 class Scanner {public : Scanner (istream&); virtual ~Scanner (); virtual Token& Scan () ; private : istream& _inputStream; }; class Parser {public : Parser (); virtual ~Parser (); virtual void Parse (Scanner&, ProgramNodeBuilder&) ; }; class ProgramNodeBuilder {public : ProgramNodeBuilder (); virtual ProgramNode* NewVariable (const char * variableName) const ; virtual ProgramNode* NewAssignment (ProgramNode* variable, ProgramNode* expression) const ; virtual ProramNode* NewReturnStatement (ProgramNode* value) const ; virtual ProgramNode* NewCondition (ProgramNode* condition, ProgramNode* truePart, ProgramNode* falsePart) const ; ProgramNode* GetRootNode () ; private : ProgramNode* _node; }; class ProgramNode {public : virtual void GetSourcePosition (int & line, int & index) ; virtual void Add (ProgramNode*) ; virtual void Remove (ProgramNode*) ; virtual void Traverse (CodeGenerator&) ; protected : ProgramNode (); }; class CodeGenerator {public : virtual void Visit (StatementNode*) ; virtual void Visit (ExpressionNode*) ; protected : CodeGenerator (BytecodeStream&); protected : BytecodeStream& _output; }; void ExpressionNode::Traverse (CodeGenerator& cg) { cg.Visit (this ); ListIterator<ProgramNode*> i (_children) ; for (i.First (); !i.IsDone (); i.Next ()) { i.CurrentItem ()->Traverse (cg); } } class Compiler {public : Compiler (); virtual void Compile (istream&, BytecodeStream&) ; }; void Compiler::Compile (istream& input, BytecodeStream& output) { Scanner scanner (input) ; ProgramNodeBuilder builder; Parser parser; parser.Parse (scanner, builder); RISCCodeGenerator generator (output) ; ProgramNode* parseTree = builder.GetRootNode (); parseTree->Traverse (generator); }
相关模式
Abstract Factory模式可以与Facade模式一起使用来提供一个 接口,这一接口可以来以一种子系统独立的方式创建子系统对象。Abstract Facotry也可以替代Facade模式隐藏那些与平台相关的类。
Mediator模式与Facade模式的相似之处是,它抽象了一些已有的类的功能。然而,Mediator的目的是对同事之间的任意通信进行抽象,通常集中不属于任何单个对象的功能。Mediator的同事对象知道中介者并与它通信,而不是直接与其他同类对象通信。相对而言,Facade模式仅对子系统对象的接口进行抽象,从而使它们更容易使用;它并不定义新功能,子系统也不知道Facade的存在。
通常来讲,仅需要一个Facade对象,因此Facade对象通常属于Singleton模式。
Flyweight(享元)
意图
运用共享技术有效地支持大量细粒度的对象。
适用性
Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。当以下情况都成立时使用Flyweight模式:
一个应用使用了大量的对象。
完全由于使用大量的对象造成很大的存储开销。
对象的大多数状态都可变为外部状态。
如果删除对象的外部状态,那么可以相对较少的共享对象取代很多组对象。
应用程序不依赖于对象标识。由于Flyweight对象可以被共享,因此对于概念上明显有别的对象,标识检测将返回真值。
协作
flyweight执行时所需的状态必定是内部的或外部的。内部状态存储于Concrete-Flyweight对象中,而外部对象则由Client对象存储或计算。当用户调用flyweight对象的操作时,将该状态传递给它。
用户不应直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Glyph {public : virtual ~Glyph (); virtual void Draw (Window*, GlyphContext&) ; virtual void SetFont (Font*, GlyphContext&) ; virtual Font* GetFont (GlyphContext&) ; virtual void First (GlyphContext&) ; virtual void Next (GlyphContext&) ; virtual bool IsDone (GlyphContext&) ; virtual Glyph* Current (GlyphContext&) ; virtual void Insert (Glyph*, GlyphContext&) ; virtual void Remove (GlyphContext&) ; protected : Glyph (); }; class Character : public Glyph {public : Character (char ); virtual void Draw (Window*, GlyphContext&) ; private : char _charcode; }; class GlyphContext {public : GlyphContext (); virtual ~GlyphContext (); virtual void Next (int step = 1 ) ; virtual void Insert (int quantity = 1 ) ; virtual Font* GetFont () ; virtual void SetFont (Font*, int span = 1 ) ; private : int _index; BTree* _fonts; }; const int NCHARCODES = 128 ;class GlyphFactory {public : GlyphFactory (); virtual ~GlyphFacotry (); virtual Character* CreateCharacter (char ) ; virtual Row* CreateRow () ; virtual Column* CreateColumn () ; private : Character* _character[NCHARCODES]; }; GlyphFactory::GlyphFactory () { for (int i = 0 ; i < NCHARCODES; ++i) { _character[i] = 0 ; } } Character* GlyphFactory::CreateCharacter (char c) { if (!_character[c]) { _character[c] = new Character (c); } return _character[c]; }
相关模式
Flyweight模式通常和Composite模式结合起来,用共享叶结点的有向无环图实现一个逻辑上的层次结构。
通常,最好用flyweight实现State和Strategy对象。
Proxy(代理)
意图
为其他对象提供一种代理以控制对这个对象的访问。
适用性
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。
远程代理 为一个对象在不同的地址空间提供局部代表。
虚代理 根据需要创建开销很大的对象。
保护代理 控制对原始对象的访问。
智能引用 取代了简单的指针,它在访问对象时执行一些附加操作。典型用途包括:
对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它。
当第一次引用一个持久对象时,将它装入内存。
在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Image ;extern Image* LoadAnImageFile (const char *) ; class ImagePtr {public : ImagePtr (const char * imageFile); virtual ~ImagePtr (); virtual Image* operator ->(); virtual Image& operator *(); private : Image* LoadImage () ; private : Image* _image; const char * _imageFile; }; ImagePtr::ImagePtr (const char * theImageFile) { _imageFile = theImageFile; _image = 0 ; } Image* ImagePtr::LoadImage () { if (_image == 0 ) { _image = LoadAnImageFile (_imageFile); } return _image; } Image* ImagePtr::operator ->() { return LoadImage (); } Image& ImagePtr::operator *() { return *LoadImage (); } int main () { ImagePtr image = ImagePtr ("anImageFileName" ); image->Draw (Point (50 , 100 )); }
相关模式
Adapter:适配器为它所适配的对象提供了一个不同的接口。相反,代理提供了与它的实体相同的接口。然而,用于访问保护的代理可能会拒绝执行实体会执行的操作,因此,它的接口实际上可能只是实体接口的一个子集。
Decorator:尽管装饰的实现部分与代理相似,但装饰的目的不一样。装饰为对象添加一个或多个功能,而代理则控制对对象的访问。
代理的实现与装饰的实现类似,但是在相似的程度上有所差别。Protection Proxy的实现可能与装饰的实现差不多。另外,Remote Proxy不包含对实体的直接引用,而只是一个间接引用,如“主机ID,主机上的局部地址”。Virtual Proxy开始的时候使用一个间接引用,例如一个文件名,但最终将获取并使用一个直接引用。
结构型模式总结
结构型模式看起来似乎都大差不差,原因可能是都是依赖于一个语言机制:单继承和多继承机制用于基于类的模式,而对象组合机制用于对象模式。
Adapter与Bridge
适配器和桥接模式都是为对象提供一层间接的联系,从而增大了灵活性。但两者是有区别的。
适配器主要是为了解决两个已有接口不兼容的问题;桥接模式是对抽象接口和它的实现部分进行桥接。
两者通常被用在软件生命周期的不同阶段。如果你发现两个==不兼容的类==必须要==同时工作==的话,就必须用适配器模式;相反 ,桥接模式使用时必须要知道:一个抽象将有多个实现部分,并且==抽象和实现两者是独立演化==的。适配器是在类已经设计好后实施,桥接模式是在设计类之前实施。
另外还有外观模式(facade)可能会与适配器混淆,facade是定义了一个新的接口,但adapter则复用一个原有的接口。
Composite、Decorator与Proxy
组合模式和装饰模式都是基于递归组合来组织可变数目的对象。不同在于,装饰模式是为了使你在==不需要生成子类==的情况下给对象==添加职责==,这避免了静态实现所有功能组合,造成子类急剧增大,浪费资源;组合模式旨在构造类,使==多个相关的对象==能够以==统一的方式处理==,而多个对象可以被当作一个对象来处理,它的重点不在于修饰,而是表示。
代理模式和装饰模式相似的地方在于它们都保留了一个指向另一个对象的指针,它们向这个对象发送请求。
不同在于,代理模式不能动态地添加或分离性质,它也不是为递归组合而设计的。它的目的是,当直接访问一个实体不方便或不符合需求时,为这个实体提供一个替代者。
行为型模式
行为型模式涉及算法和对象间职责的分配。行为型模式不仅描述对象或类的模式,还描述它们之间的通信模式。
Chain of Responsibility(职责链)
意图
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
适用性
有多个对象可以处理一个请求,哪个对象处理该请求运行时自动确定。
你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
可处理一个请求的对象集合应被动态指定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 typedef int Topic;const Topic NO_HELP_TOPIC = -1 ;class HelpHandler {public : HelpHandler (HelpHandler* = nullptr , Topic = NO_HELP_TOPIC); virtual bool HasHelp () ; virtual void SetHandler (HelpHandler*, Topic) ; virtual void HandlerHelp () ; private : HelpHandler* _successor; Topic _topic; }; HelpHandler::HelpHandler (HelpHandler* h, Topic t): _successor(h), _topic(t) { } bool HelpHandler::HasHelp () { return _topic != NO_HELP_TOPIC; } void HelpHandler::HandleHelp () { if (_successor != 0 ) { _successor->HandleHelp (); } } class Widget : public HelpHandler {protected : Widget (Widget* parent, Topic t = NO_HELP_TOPIC); private : Widget* _parent; }; Widget::Widget (Widget* w, Topic t) : HelpHandler (w, t) { _parent = w; } class Button : public Widget {public : Button (Widget* d, Topic t = NO_HELP_TOPIC); virtual void HandleHelp () ; }; Button::Button (Widget* h, Topic t) : Widget (h, t) { } void HandleHelp () { if (HasHelp ()) { } else { HelpHandler::HandleHelp (); } }
相关模式
职责链常与组合模式一起使用。这种情况下,一个构件的父构件可作为它的后继。
Command(命令)
意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
适用性
当你有如下需求时,可使用Command模式
抽象出待执行的动作以参数化某对象。你可用过程语言中的回调函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command模式是回调机制的一个面向对象的替代品。
在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那里实现该请求。
支持取消操作。Command的Execute操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute和Execute来实现重数不限的“撤销”和“重做”。
支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。
用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务的信息系统中很常见。一个事务封装了对数据的一组变动。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。
协作
Client创建一个ConcreteCommand对象并指定它的Receiver对象。
某Invoker对象存储该ConcreteCommand对象。
某Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用来取消该命令。
ConcreteCommand对象调用它的Reciver的一些操作以执行该请求。
效果
Command模式将调用操作的对象与知道如何实现该操作的对象解耦。
Command是头等的对象。它们可像其他的对象一样被操纵和扩展。
你可将多个命令装配成一个组合命令。
增加新的Command很容易,因为这无须改变已有的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 class Command { virtual ~Command (); virtual void Execute () = 0 ; protected : Command (); }; class OpenCommand : public Command {public : OpenCommand (Application*); virtual void Execute () ; protected : virtual const char * AskUser () ; private : Application* _application; char * _response; }; OpenCommand::OpenCommand (Application* a) { _application = a; } void OpenCommand::Execute () { const char * name = AskUser (); if (name != 0 ) { Document* document = new Document (name); _application->Add (document); document->Open (); } } class PasteCommand : public Command {public : PasteCommand (Document*); virtual void Execute () ; private : Document* _document; }; PasteCommand::PasteCommand (Document* doc) { _document = doc; } void PasteCommand::Execute () { _document->Paste (); } template <class Receiver >class SimpleCommand : public Command {public : typedef void (Receiver::* Action) () ; SimpleCommand (Receiver* r, Action a): _receiver(r), _action(a) { } virtual void Execute () ; private : Action _action; Receiver* _receiver; }; template <class Receiver >void SimpleCommand<Receiver>::Execute () { (_receiver->*_action)(); } MyClass* receiver = new MyClass; Command* aCommand = new SimpleCommand <MyClass>(receiver, &MyClass::Action); aCommand->Execute (); class MacroCommand : public Command {public : MacroCommand (); virtual void Add (Command*) ; virtual void Remove (Command*) ; virtual void Execute () ; private : List<Command*>* _cmds; }; void MacroCommand::Execute () { List<Command*>* i (_cmds) ; for (i.First (); !i.IsDone (); i.Next ()) { Command* c = i.CurrentItem (); c->Execute (); } } void MacroCommand::Add (Command* c) { _cmds->Append (c); } void MacroCommand::Remove (Command* c) { _cmds->Remove (c); }
相关模式
Composite可用来实现宏命令。
Memento可用来保持某个状态,命令用这一状态来取消它的效果。
在被放入历史列表前必须被拷贝的命令起到一种原型的作用。
思考
没太懂这个模式,待补充思考
Interpreter(解释器)
意图
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
适用性
当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
文法简单。对于复杂的文法,文法的类曾次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无须构建抽象语法树即可解释表达式,这样可以节省空间而且还可能节省时间。
效率不是关键问题。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器也可用解释器模式实现,该模式仍是可用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 class BooleanExp {public : BooleanExp (); virtual ~BooleanExp (); virtual bool Evaluate (Context&) = 0 ; virtual BooleanExp* Replace (const char *, BooleanExp&) = 0 ; virtual BooleanExp* Copy () const = 0 ; }; class Context {public : bool Lookup (const char *) const ; void Assign (VariableExp*, bool ) ; }; class VariableExp : public BooleanExp {public : VariableExp (const char *); virtual ~VariableExp (); virtual bool Evaluate (Context&) ; virtual BooleanExp* Replace (const char *, BooleanExp&) ; virtual BooleanExp* Copy () ; private : char * _name; }; VariableExp::VariableExp (const char * name) { _name = strdup (name); } bool VariableExp::Evaluate (Context& aContext) { return aContext.Lookup (_name); } BooleanExp* Variable::Copy () const { return new VariableExp (_name); } BooleanExp* Variable::Replace (const char * name, BooleanExp& exp) { if (strcmp (name, _name) == 0 ) { return exp.Copy (); } else { return new VariableExp (_name); } } class AndExp : public BooleanExp {public : AndExp (BooleanExp*, BooleanExp*); virtual ~AndExp (); virtual bool Evaluate (Context&) ; virtual BooleanExp* Replace (const char *, BooleanExp&) ; virtual BooleanExp* Copy () const ; private : BooleanExp* _operand1; BooleanExp* _operand2; }; AndExp::AndExp (BooleanExp* op1, BooleanExp* op2) { _operand1 = op1; _operand2 = op2; } bool AndExp::Evaluate (Context& aContext) { return _operand1->Evaluate (aContext) && operand2->Evaluate (aContext); } BooleanExp* AndExp::Copy () const { return new AndExp (_operand1->Copy (), _operand2->Copy ()); } BooleanExp* AndExp::Replace (const char * name, BooleanExp& exp) { return new AndExp (_operand1->Replace (name, exp), _operand2->Replace (name, exp)); } int main () { BooleanExp* expression; Context context; VariableExp* x = new VariableExp ("X" ); VariableExp* x = new VariableExp ("Y" ); expression = new OrExp (new AndExp (new Constant (true ), x), new AndExp (y, new NotExp (x))); context.Assign (x, false ); context.Assign (y, true ); bool result = expression->Evaluate (context); VariableExp* z = new VariableExp ("Z" ); NotExp not_z (z) ; BooleanExp* replacement = expression->Replace ("Y" , not_z); context.Assin (z, true ); result = replacement->Evaluate (context); }
相关模式
Composite:抽象语法树是一个组合模式的实例。
Flyweight:说明了如何在抽象语法树中共享终结符。
Iterator:解释器可用一个迭代器遍历该结构。
Visitor:可用来在一个类中维护抽象语法树中各结点的行为。
Iterator(迭代器)
意图
提供一种方法顺序访问一个聚合对象中的各个元素,而不需要暴露该对象的内部表示。
适用性
访问一个聚合对象的内部而无须暴露它的内部表示。
支持对聚合对象的多种遍历。
为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 template <typename Item>class List {public : List (long size = DEFAULT_LIST_CAPACITY); long Count () const ; Item& Get (long index) const ; }; template <typename Item>class Iterator {public : virtual void First () = 0 ; virtual void Next () = 0 ; virtual bool IsDone () const = 0 ; virtual Item CurrentItem () const = 0 ; protected : Iterator (); }; template <typename Item>class ListIterator : public Iterator<Item> {public : ListIterator (const List<Item>* aList); virtual void First () ; virtual void Next () ; virtual bool IsDone () const ; virtual Item CurrentItem () const ; private : const List<Item>* _list; long _current; }; template <typename Item>ListIterator<Item>::ListIterator (const List<Item>* aList) : _list(aList), _current(0 ) { } template <typename Item>void ListIterator<Item>::First () { _current = 0 ; } template <typename Item>void ListIterator<Item>::Next () { _current++; } template <typename Item>bool ListIterator<Item>::IsDone () const { return _current >= _list->Count (); } template <typename Item>Item ListIterator<Item>::CurrentItem () const { if (IsDone ()) { throw IteratorOutOfBounds; } return _list->Get (_current); } void PrintEmployees (Iterator<Employee*>& i) { for (i.First (); !i.IsDone (); i.Next ()) { i.CurrentItem ()->Print (); } }
相关模式
Composite:迭代器常被应用到像组合这样的递归结构上。
Factory Method:多态迭代器靠Factory Method来实例化适当的迭代器子类。
Memento:常与迭代器模式一起使用。迭代器可使用memento来捕获一个迭代的状态。迭代器在其内部存储memento。
意图
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间地交互。
适用性
一组对象以定义良好但复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
想定制一个分布在多个类中的行为,而又不想生成太多子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 class DialogDirector {public : virtual ~DialogDirector (); virtual void ShowDialog () ; virtual void WidgetChanged (Widget*) = 0 ; protected : DialogDirector (); virtual void CreateWidgets () = 0 ; }; class Widget {public : Widget (DialogDirector*); virtual void Changed () ; virtual void HandleMouse (MouseEvent& event) ; private : DialogDirector* _director; }; void Widget::Changed () { _director->WidgetChanged (this ); } class ListBox : public Widget {public : ListBox (DialogDirector*); virtual void SetList (List<char *>* listItems) ; virtual void HandleMouse (MouseEvent& event) ; }; class EntryField : public Widget {public : EntryField (DialogDirector*); virtual void SetText (const char * text) ; virtual const char * GetText () ; virtual void HandleMouse (MouseEvent& event) ; }; class Button : public Widget {public : Button (DialogDirector*); virtual void SetText (const char * text) ; virtual void HandleMouse (MouseEvent& event) ; }; void Button::HandleMouse (MouseEvent& event) { Changed (); } class FontDialogDirector : public DialogDirector {public : FontDialogDirector (); virtual ~FontDialogDirector (); virtual void WidgetChanged (Widget*) ; protected : virtual void CreateWidgets () ; private : Button* _ok; Button* _cancel; ListBox* _fontList; EntryField* _fontName; }; void FontDialogDirector::CreateWidgets () { _ok = new Button (this ); _cancel = new Button (this ); _fontList = new ListBox (this ); _fontName = new EntryField (this ); } void FontDialogDirector::WidgetChanged (Widget* theChangedWidget) { if (theChangedWidget == _fontList) { _fontName->SetText (_fontList->GetSelection ()); } else if (theChangedWidget == _ok) { } else if (theChangedWidget == _cancel) { } }
相关模式
Facade与中介者的不同之处在于,它是对一个对象子系统进行抽象,从而提供了一个更为方便的接口。它的协议是单向的,即Facade对象对这个子系统类提出请求,但反之则不行。相反,Mediator提供了各Colleague对象不支持或不能支持的协作行为,而且协议是多向的。
Colleague可使用Observer模式与Mediator通信。
Memento(备忘录)
意图
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
适用性
必须保存一个对象在某个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
如果一个接口让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Graphic ;class MoveCommand {public : MoveCommand (Graphic* target, const Point& delta); void Execute () ; void Unexecute () ; private : ConstraintSolverMemento* _state; Point _delta; Graphic* _target; }; class ConstraintSolver {public : static ConstraintSolver* Instance () ; void Solve () ; void AddConstraint (Graphic* startConnection, Graphic* endConnection) ; ConstraintSolverMemento* CreateMmento () ; void SetMemento (ConstraintSolverMemento*) ; private :}; class ConstraintSolverMemento {public : virtual ~ConstraintSolverMemento (); private : friend class ConstraintSolver ; ConstraintSolverMemento (); }; void MoveCommand::Execute () { ConstraintSolver* solver = ConstraintSolver::Instance (); _state = solver->CreateMemento (); _target->Move (_delta); solver->Solve (); } void MoveCommand::Unexecute () { ConstraintSolver* solver = ConstraintSolver::Instance (); _target->Move (-_delta); solver->SetMemento (_state); solver->Solve (); }
相关模式
Command:命令可使用备忘录来为可撤销的操作维护状态。
Iterator:如前所示,备忘录可用于迭代。
Observer(观察者)
意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
适用性
一个对象模型有两个方面,其中一个方面依赖于另一方面。将这两者封装在独立的对象中,以使他们可以各自独立地改变和复用。
对一个对象的改变需要改变其他对象,而不知道具体有多少对象有待改变。
一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,你不希望这些对象是紧密耦合的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 class Subject ;class Observer {public : virtual ~Observer (); virtual void Update (Subject* theChangedSubject) = 0 ; protected : Observer (); }; class Subject {public : virtual ~Subject (); virtual void Attach (Observer*) ; virtual void Detach (Observer*) ; virtual void Notify () ; protected : Subject (); private : List<Observer*>* _observers; }; class ClockTimer : public Subject {public : ClockTimer (); virtual int GetHour () ; virtual int GetMinute () ; virtual int GetSecond () ; void Tick () ; }; void ClockTimer::Tick () { Notify (); } class DigitalClock : public Widget, public Observer {public : DigitalClock (ClockTimer*); virtual ~DigitalClock (); virtual void Update (Subject*) override ; virtual void Draw () override ; private : ClockTimer* _subject; }; DigitalClock::DigitalClock (ClockTimer* s) { _subject = s; _subject->Attach (this ); } DigitalClock::~DigitalClock () { _subject->Detach (this ); } void DigitalClock::Update (Subject* theChangedSubject) { if (theChangedSubject == _subject) { Draw (); } } void DigitalClock::Draw () { int hour = _subject->GetHour (); int minute = _subject->GetMinute (); }
相关模式
Mediator:通过封装复杂的更新语义,ChangeManager充当目标和观察者之间的中介者。
Singleton:ChangeManager可使用Singleton模式来保证它是唯一的并且是可全局访问的。
State(状态)
意图
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
适用性
一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件状态。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 class TCPOctetStream ;class TCPState ;class TCPConnection {public : TCPConnection (); void ActiveOpen () ; void PassiveOpen () ; void Close () ; void Send () ; void Acknowledge () ; void Synchronize () ; void ProcrssOctet (TCPOctetStream*) ; private : friend class TCPState ; void ChangeState (TCPState*) ; private : TCPState* _state; }; class TCPState {public : virtual void Transmit (TCPConnection*, TCPOctetStream*) ; virtual void ActiveOpen (TCPConnection*) ; virtual void PassiveOpen (TCPConnection*) ; virtual void Close (TCPConnection*) ; virtual void Synchronize (TCPConnection*) ; virtual void Acknowledge (TCPConnection*) ; virtual void Send (TCPConnection*) ; protected : void ChangeState (TCPConnection*, TCPState*) ; }; TCPConnection::TCPConnection () { _state = TCPClosed::Instance (); } void TCPConnection::ChangeState (TCPState* s) { _state = s; } void TCPConnection::ActiveOpen () { _state->ActiveOpen (this ); } void TCPConnection::PassiveOpen () { _state->PassiveOpen (this ); } void TCPConnection::Close () { _state->Close (this ); } void TCPConnection::Ackonwledge () { _state->Acknowledge (this ); } void TCPConnection::Synchronize () { _state->Synchronize (this ); } void TCPState::Transmit (TCPConnection*, TCPOctetStream*) { }void TCPState::ActiveOpen (TCPConnection*) { }void TCPState::PassiveOpen (TCPConnection*) { } void TCPState::Close (TCPConnection*) { }void TCPState::Synchronize (TCPConnection*) { }void TCPState::ChangeState (TCPConnection* t, TCPState* s) { t->ChangeState (s); } class TCPEstablished : public TCPState {public : static TCPState* Instance () ; virtual void Transmit (TCPConnection*, TCPOctetStream*) ; virtual void Close (TCPConnection*) ; }; class TCPListen : public TCPState {public : static TCPState* Instance () ; virtual void Send (TCPConnection*) ; }; class TCPClosed : public TCPState {public : static TCPState* Instance () ; virtual void ActiveOpen (TCPConnection*) ; virtual void PassiveOpen (TCPConnection*) ; }; void TCPClosed::ActiveOpen (TCPConnection* t) { ChangeState (t, TCPEstablished::Instance ()); } void TCPClosed::PassiveOpen (TCPConnection* t) { ChangeState (t, TCPListen::Instance ()); } void TCPEstablished::Close (TCPConnection* t) { ChangeState (t, TCPListen::Instance ()) } void TCPEstablished::Transmit (TCPConnection* t, TCPOctetStream* o) { t->ProcessOctet (o); } void TCPListen::Send (TCPConnection* t) { ChangeState (t, TCPEstablished::Instance ()); }
相关模式
Flyweight解释了何时以及怎样共享状态对象。
状态对象通常是Singleton。
Strategy(策略)
意图
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
适用性
许多相关的类仅仅是行为有异,“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
算法使得客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Composition {public : Composition (Compositor*); void Repair () ; private : Compositor* _compositor; Component* _components; int _componentCount; int _lineWidth; int * _lineBreaks; int _lineCount; }; class Compositor {public : virtual int Compose (Coord natural[], Coord streth[], Coord shrink[], int componentCount, int lineWidth, int breaks[]) = 0 ; protected : Compositor (); }; void Composition::Repair () { Coord* natural; Coord* stretchability; Coord* shrinkability; int componentCount; int * breaks; int breakCount; breakCount = _compositor->Compose (natural, strtchability, shrinkability, componentCount, _lineWidth, breaks); } class SimpleCompositor : public Compositor {public : SimpleCompositor (); virtual int Compose (Coord natural[], Coord streth[], Coord shrink[], int componentCount, int lineWidth, int breaks[]) ; }; class TeXCompositor : public Compositor {public : TeXCompositor (); virtual int Compose (Coord natural[], Coord streth[], Coord shrink[], int componentCount, int lineWidth, int breaks[]) ; }; Composition* quick = new Composition (new SimpleCompositor); Composition* slick = new Composition (new TeXCompositor);
相关模式
Flyweight:Strategy对象经常是很好的轻量级对象。
Template Method(模板方法)
意图
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
适用性
一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
各子类中公共的行为应该被提取出来并集中到一个公共父类中以避免代码重复。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用新的操作的模板方法来替换不同的代码。
控制子类扩展。模板方法只在特定调用钩子操作,这样就只允许在这些点进行扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void View::Display () { SetFocus (); DoDisplay (); ResetFocus (); } void View::DoDisplay () { }void MyView::DoDisplay () { }
相关模式
Facotry Method常被模板方法调用。
Strategy:模板方法使用继承来改变算法的一部分,Strategy使用委托来改变整个算法。
Visitor(访问者)
意图
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用性
一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象机构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作比较好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 class Equipment {public : virtual ~Equipment (); const char * Name () {return _name;} virtual Watt Power () ; virtual Currency NetPrice () ; virtual Currency DiscountPrice () ; virtual void Accept (EquipmentVisitor&) ; protected : Equipment (const char *); private : const char * _name; }; class EquipmentVisitor {public : virtual ~EquipmentVisitor (); virtual void VisitFloppyDisk (FloppyDisk*) ; virtual void VisitCard (Card*) ; virtual void VisitChassis (Chassis*) ; virtual void VisitBus (Bus*) ; protected : EquipmentVisitor (); }; void FloppyDisk::Accpet (EquipmentVisitor& visitor) { visitor.VisitFloppyDisk (this ); } void Chassis::Accept (EquipmentVisitor& visitor) { for (ListIterator<Equipment*> i (_parts); !i.IsDone (); i.Next ()) { i.CurrentItem ()->Accept (visitor); } visitor.VisitChassis (this ); } class PricingVisitor : public EquipmentVisitor {public : PricingVisitor (); Currency& GetTotalPrice () ; virtual void VisitFloppyDisk (FloppyDisk*) ; virtual void VisitCard (Card*) ; virtual void VisitChassis (Chassis*) ; virtual void VisitBus (Bus*) ; private : Currency _total; }; void PricingVisitor::VisitFloppyDisk (FloppyDisk* e) { _total += e->NetPrice (); } void PricingVisitor::VisitChassis (Chassis* e) { _total += e->DiscountPrice (); } class InventoryVisitor : public EquipmentVisitor {public : InventoryVisitor (); Inventory& GetInventory () ; virtual void VisitFloppyDisk (FloppyDisk*) ; virtual void VisitCard (Card*) ; virtual void VisitChassis (Chassis*) ; virtual void VisitBus (Bus*) ; private : Inventory _inventory; }; void InventoryVisitor::VisitFloppyDisk (FloppyDisk* e) { _inventory.Accumulate (e); } void InventoryVisitor::VisitChassis (Chassis* e) { _inventory.Accumulate (e); } Equipment* component; InventoryVisitor visitor; component->Accept (visitor); cout << "Inventory " << component->Name () << visitor.GetInventory ();
相关模式
Composite:访问者可以用于一个由Composite模式定义的对象结构进行操作。
Interpreter:访问者可以用于解释。
行为型模式总结
封装变化
当一个程序某方面的特征经常发生变化时,这些模式就定义一个封装这方面的对象。这样当该程序的其他部分依赖于这方面时,它们都可以与此对象协作。
一个Strategy对象封装了一个算法。
一个State对象封装了一个与状态相关的行为。
一个Mediator对象封装了对象间的协议。
一个Iterator对象封装了访问和遍历一个聚集对象中的各个构件的方法。
但不是所有的对象行为模式都像这样分割功能,例如,职责链模式可以处理任意数量的对象(一条链上),而所有的对象可能已经存在于系统中了。
职责链说明了行为模式间的另一个不同点:并非所有的行为模式都定义类之间的静态通讯关系。职责链提供在数目可变的对象间进行通信的机制。其他模式涉及一些作为参数传递的对象。
对象作为参数
一些模式引入总是被用作参数的对象,例如访问者模式,一个访问者对象是一个多态的Accept操作的参数,这个操作作用于该访问者对象访问的对象。
其他模式定义一些可作为令牌到处传递的对象,这些对象将在稍后被调用,如命令模式和备忘录模式。
通信应该被封装还是被分布
中介者模式和观察者模式是相互竞争的模式。它们之间的差别在于,观察者模式通过引入Observer和Subject对象来分布通信,而Mediator对象则封装了其他对象间的通信。