设计模式总结
解决问题负责问题的思路
A、分而治之(分界)
大问题化为多个小问题,复杂问题分解为多个简单问题。
B、简而治之(抽象)
忽视非本质细节,抽取为微内核对象模型。
1、八大面向对象设计原则
a、依赖倒置原则(DIP)
-
高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)。
-
抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。
b、 开放封闭原则(OCP)
-
对扩展开放,对更改封闭。(不更改已有类文件,新增文件进行扩展)
-
类模块可扩展,但是不可修改。
c、单一职责原则(SRP)
-
一个类应该仅有一个引起变化的原因。
-
变化的方向隐含着类的责任。
d、Liskov替换原则(LSP)
- 子类必须能够替换它们的基类(IS-A)
- 继承表达类型抽象
e、接口隔离原则(ISP)
- 不应该强迫客户程序依赖它们不用的方法。
- 接口应该小而完备。
f、优先使用对象组合,而不是类继承
- 类继承通常为“白箱复用”,对象组合通常为”黑箱复用“。
- 继承某种程度上破坏了封装性,子类父类耦合度高。
- 对象组合只要求被组合的对象具有良好定义的外部接口,耦合度低。
g、封装变化点
- 使用封装创建对象之间的分界层,让设计者可以在分界的一侧进行修改,而不会对另外一侧产生不良影响,从而实现层次间的松耦合。
h、针对接口编程而不是针对实现编程
- 不将变量类型声明为某个特定的具体类,而是生命为某个接口。
- 客户程序无需知道对象的具体类型,只需知道对象所具有的接口。
- 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案。
2、设计模式
2.1 创建型模式
2.1.1 Simple Factory(普通工厂)——对象创建型模式
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4OfwO2Y9-1652451824231)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220417204458212.png)]
-
代码
//Operation.h #ifndef OPERATION_H_ #define OPERATION_H_ class Operation { public: Operation(void); ~Operation(void); virtual double GetResult(); void SetNumA(double fValueA); void SetNumB(double fValueB); protected: double m_fNumberA; double m_fNumberB; }; #endif //Operation.cpp #include "Operation.h" Operation::Operation(void) : m_fNumberA(0) , m_fNumberB(0) { } Operation::~Operation(void) { } double Operation::GetResult() { double fResult = 0; return fResult; } void Operation::SetNumA(double fValueA) { m_fNumberA = fValueA; } void Operation::SetNumB(double fValueB) { m_fNumberB = fValueB; } //Add.h #ifndef ADD_H_ #define ADD_H_ #include "Operation.h" class Add : public Operation { public: Add(void); ~Add(void); virtual double GetResult(); }; #endif //Add.cpp #include "Add.h" Add::Add(void) { } Add::~Add(void) { } double Add::GetResult() { double fResult = 0; fResult = m_fNumberA + m_fNumberB; return fResult; } //Subtract.h #ifndef SUBTRACT_H_ #define SUBTRACT_H_ #include "Operation.h" class Subtract : public Operation { public: Subtract(void); ~Subtract(void); virtual double GetResult(); }; #endif //Subtract.cp #include "Subtract.h" Subtract::Subtract(void) { } Subtract::~Subtract(void) { } double Subtract::GetResult() { double fResult = 0; fResult = m_fNumberA - m_fNumberB; return fResult; } //Multi.h #ifndef MULTIPLY_H_ #define MULTIPLY_H_ #include "Operation.h" class Multiply : public Operation { public: Multiply(void); ~Multiply(void); virtual double GetResult(); }; #endif //multi.cpp #include "Multiply.h" Multiply::Multiply(void) { } Multiply::~Multiply(void) { } double Multiply::GetResult() { double fResult = 0; fResult = m_fNumberA * m_fNumberB; return fResult; } //Divide.h #ifndef DIVIDE_H_ #define DIVIDE_H_ #include "Operation.h" class Divide : public Operation { public: Divide(void); ~Divide(void); virtual double GetResult(); }; #endif //Divide.cpp #include "Divide.h" Divide::Divide(void) { } Divide::~Divide(void) { } double Divide::GetResult() { double fResult = 0; if (m_fNumberB != 0) { fResult = m_fNumberA / m_fNumberB; } return fResult; } //Factory.h #ifndef OPERATION_FACTORY_H_ #define OPERATION_FACTORY_H_ #include "Operation.h" enum OperationType { OperationType_Start, OperationType_Add = OperationType_Start, OperationType_Sub, OperationType_Multi, OperationType_Divide, OperationType_MAX, }; class OperationFactory { public: OperationFactory(void); ~OperationFactory(void); Operation* CreateOperation(OperationType eType); private: Operation* m_pOperation; }; #endif //Factory.cpp #include "OperationFactory.h" #include "Add.h" #include "Subtract.h" #include "Multiply.h" #include "Divide.h" OperationFactory::OperationFactory(void) : m_pOperation(0) { } OperationFactory::~OperationFactory(void) { if (0 != m_pOperation) { delete m_pOperation; m_pOperation = 0; } } Operation* OperationFactory::CreateOperation(OperationType eType) { switch (eType) { case OperationType_Add: m_pOperation = new Add(); break; case OperationType_Sub: m_pOperation = new Subtract(); break; case OperationType_Multi: m_pOperation = new Multiply(); break; case OperationType_Divide: m_pOperation = new Divide(); break; default: break; } return m_pOperation; } //main.cpp #include "Add.h" #include "Divide.h" #include "Subtract.h" #include "Multiply.h" #include "OperationFactory.h" #include "stdio.h" int main() { OperationFactory* pFactory = new OperationFactory(); Operation* pOperation = pFactory->CreateOperation(OperationType_Add); pOperation->SetNumA(3.14); pOperation->SetNumB(2.43); float fResult = pOperation->GetResult(); printf("The value of the result: %f2", fResult); return 0; }
-
相关模式分析
与策略模式对比分析
工厂模式
public class OperationFactory { public static Operation CreateOperate (string operate) { Operation oper=null; switch (operate) { case "+": oper = new OperationAdd(); break; case "-": oper = new OperationSub(); break; case "*": oper = new OperationMul(); break; case "/": oper = new OperationDiv(); break; default: oper = new Operation(); break; } return oper; } }
策略模式
class Context { CashSuper csuper; public Context(CashSuper cs) { this.csuper = cs; } public double GetResult(double money) { //调用具体策略类的收费方法 return csuper.acceptCash(money); } }
1.从类型上说:简单工厂模式属于创建型模式,而策略模式属于行为型模式。
2.接下来,看一个小例子:斧子有很多种,有一个工厂专门负责生产各种需求的斧子。
工厂模式:
1)根据你给出的目的来生产不同用途的斧子,例如要砍人,那么工厂生产砍人斧子,要伐木就生产伐木斧子。
2)即根据你给出一些属性来生产不同行为的一类对象返回给你。
3)关注对象创建
策略模式:
1)用工厂生产的斧子来做对应的事情,例如用砍人的斧子来砍人,用伐木的斧子来伐木。
2)即根据你给出对应的对象来执行对应的方法。
3)关注行为的选择
3.简单工厂模式:根据客户选择的条件,来帮客户创建一个对象。
策略模式:客户给它一个创建好的对象,它来帮客户做相应的事。
2.1.2 Factory Method(工厂方法)——对象创建型模式
-
意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到子类。
-
动机
框架使用抽象类定义和维护对象之间的关系。这些对象的创建通常也由框架负责。考虑这样一个应用框架,它可以向用户显示多个文档,在这个框架中,两个主要的抽象类是Application和Document。这两个类都是抽象的,客户必须通过它们的子类来做与具体应用相关的实现。例如,为创建一个绘图应用,我们定义类DrawingApplication和DrawingDocument。Application负责管理Document并根据需要创建它们。因此被实例化的特定Document与特定应用相关,所以Application类不可能预测到哪个子类将被实例化。Factory Method提供了一个解决方案。它封装了哪个Document子类将被创建的信息并将这些信息从该框架中分离出来。
-
适用性
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类指定创建对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理这这一信息局部化的时候。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRPs9zAO-1652451824233)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220418223046925.png)]
-
参与者
- Product:定义工厂方法所创建的对象的接口。
- ConcreteProduct:实现Product接口。
- Creator:声明一个工厂方法,该方法返回一个product对象;可以调用工厂方法创建一个对象。
- ConcreteCreator:重新定义工厂方法。
-
效果
- 为子类提供钩子操作,用工厂方法在一个类的内部创建对象通常比直接创建对象更为灵活。
- 连接平行的层次。当一个类将它的一些职责委托给一个独立的类时,就产生了平行类层次的情况。
-
代码示例
//Creator.h #ifndef CREATOR_H_ #define CREATOR_H_ class IProduct; enum ProductType { ProductType_Start, ProductType_Me = ProductType_Start, ProductType_You, ProductType_Max, }; class Creator { public: Creator(void); virtual ~Creator(void); //参数工厂方法 virtual IProduct* GetProduct(ProductType eType); virtual IProduct* Create(ProductType eProductType = ProductType_Max); private: IProduct* m_pProduct; }; #endif //Creator.cpp #include "Creator.h" #include "MyProductA.h" #include "YourProduct.h" Creator::Creator(void) :m_pProduct(0) { } Creator::~Creator(void) { delete m_pProduct; m_pProduct = 0; } IProduct* Creator::GetProduct(ProductType eType) { if (0 == m_pProduct) { m_pProduct = Create(eType); } return m_pProduct; } IProduct* Creator::Create(ProductType eProductType /*= ProductType_Max*/) { if (ProductType_Me == eProductType) { return new MyProductA(); } else if (ProductType_You == eProductType) { return new YourProduct(); } } //SpecialCreator.h #include "Creator.h" class SpecialCreator : public Creator { public: SpecialCreator(void); virtual ~SpecialCreator(void); virtual IProduct* Create(ProductType eProductType /* = ProductType_Max */); }; //SpecialCreator.cpp #include "SpecialCreator.h" #include "MyProductA.h" #include "YourProduct.h" SpecialCreator::SpecialCreator(void) { } SpecialCreator::~SpecialCreator(void) { } IProduct* SpecialCreator::Create(ProductType eProductType /* = ProductType_Max */) { if (ProductType_Me == eProductType) { return new YourProduct; } else if (ProductType_You == eProductType) { return new MyProductA; } } //StandardCreator.h #ifndef STANDARD_CREATOR_H_ #define STANDARD_CREATOR_H_ #include "Creator.h" template<class TheProduct> class StandardCreator : public Creator { public: StandardCreator(void); virtual ~StandardCreator(void); virtual IProduct* Create(ProductType eType); }; template<class TheProduct> StandardCreator<TheProduct>::~StandardCreator(void) { } template<class TheProduct> StandardCreator<TheProduct>::StandardCreator(void) { } template<class TheProduct> IProduct* StandardCreator<TheProduct>::Create(ProductType eType) { return new TheProduct; } #endif //StandardCreator.cpp #include "StandardCreator.h" //Iproduct.h #ifndef IPRODUCT_H_ #define IPRODUCT_H_ class IProduct { public: IProduct(void); virtual ~IProduct(void); }; #endif //Iproduct.cp #include "IProduct.h" IProduct::IProduct(void) { } IProduct::~IProduct(void) { } //MyProductA.h #ifndef MY_PRODUCT_H_ #define MY_PRODUCT_H_ #include "IProduct.h" class MyProductA : public IProduct { public: MyProductA(void); virtual ~MyProductA(void); }; #endif //MyProductA.cpp #include "MyProductA.h" #include <stdio.h> MyProductA::MyProductA(void) { printf("MyProductA Created!\n"); } MyProductA::~MyProductA(void) { printf("MyProductA Destroyed!\n"); } //YourProduct.h #ifndef YOUR_PRODUCT_H_ #define YOUR_PRODUCT_H_ #include "IProduct.h" class YourProduct : public IProduct { public: YourProduct(void); virtual ~YourProduct(void); }; #endif //YourProduct.cpp #include "YourProduct.h" #include <stdio.h> YourProduct::YourProduct(void) { printf("YourProduct Created!\n"); } YourProduct::~YourProduct(void) { printf("YourProduct Destroyed!\n"); } //main.cpp #include "Creator.h" #include "YourProduct.h" #include "MyProductA.h" #include "SpecialCreator.h" #include "StandardCreator.h" #include <process.h> int main() { IProduct* pProduct = 0; Creator* pCreator = 0; pCreator = new Creator(); //使用第一种工厂方法所见即所得 pProduct = pCreator->Create(ProductType_Me); delete pCreator; pCreator = 0; //使用第二种工厂方法,所见非所得 pCreator = new SpecialCreator(); pProduct = pCreator->Create(ProductType_Me); delete pCreator; pCreator = 0; //使用第三种工厂方法,模板方法 pCreator = new StandardCreator<MyProductA>(); pProduct = pCreator->Create(); delete pCreator; pCreator = 0; pCreator = new StandardCreator<YourProduct>(); pProduct = pCreator->Create(); delete pCreator; pCreator = 0; system("pause"); return 0; }
-
相关模式
2.1.3 Abstract Factory(抽象工厂)——对象创建型模式
-
意图
提供一个接口,创建一系列相关或相互依赖的对象,而无须指定他们具体的类。
-
动机
考虑一个支持多种视感标准的用户界面工具包。不同的视感风格为诸如滚动条、窗口和按钮等用户界面窗口组件定义不同的外观和行为。为保证视感风格标准之间的可移植性,一个应用不应该为一个特定的视感分割外观硬编码它的窗口组件。在整个应用中实例化特定视感风格的窗口组件类,使得以后很难改变视感风格。
为解决这一问题,我们可以定义一个抽象的WidgetFactory类,这个类声明了一个用来创建每一类基本窗口的接口。每一类窗口组件都有一个抽象类,而具体子类实现了窗口组件的特定视感风格。
-
适用性
- 一个系统要独立于它的产品创建、组合和表示。
- 一个系统要由多个产品系列中的一个配置。
- 强调一系列相关的产品对象的设计以便进行联合使用。
- 提供一个产品类库,但只想显示它们的接口而不是实现。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nO0SSPS2-1652451824234)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220419074442558.png)]
-
参与者
- AbstractFfactory:声明一个创建一个抽象产品对象的操作接口。
- ConcreteFactory:实现具体创建产品对象的操作。
- AbstractProduct:为一类产品对象声明一个接口。
- ConcreteProduct:定义一个将被相应的具体工厂创建的产品对象;实现AbstractProduct接口。
- Client:使用AbstractFactory和AbstaractProduct类声明的接口。
-
效果
- 它分离了具体的类。帮助你控制一个应用创建的对象的类。因为一个工厂封装创建产品对象的责任和过程,他将客户与类的实现分离。客户抽象接口操作实例。
- 是的易于更换产品系列。
- 有利于产品的一致性。
- 难以支持新种类的产品。
-
代码示例
#ifndef MAZE_FACTORY_H_ #define MAZE_FACTORY_H_ class IMaze; class IWall; class IRoom; class IDoor; class MazeFactory { public: MazeFactory(void); virtual IMaze* MakeMaze() const {return new IMaze;}; virtual IWall* MakeWall() const {return new IWall;}; virtual IRoom* MakeRoom(int nRoomNo) const {return new IRoom(nRoomNo);}; virtual IDoor* MakeDoor(IRoom* pRoom1, IRoom* pRoom2) const {return new IDoor(pRoom1, pRoom2);}; ~MazeFactory(void); }; #endif #include "MazeFactory.h" class BombedFactory : public MazeFactory { public: BombedFactory(void); ~BombedFactory(void); IWall* MakeWall(); IDoor* MakeDoor(IRoom* pRoom1, IRoom* pRoom2); }; #include "BombedFactory.h" #include "BombedDoor.h" #include "BombedWall.h" BombedFactory::BombedFactory(void) { } BombedFactory::~BombedFactory(void) { } IWall* BombedFactory::MakeWall() { return new BombedWall; } IDoor* BombedFactory::MakeDoor(IRoom* pRoom1, IRoom* pRoom2) { return new BombedDoor; } #ifndef ENCHANTED_MAZE_FACTORY_H_ #define ENCHANTED_MAZE_FACTORY_H_ #include "MazeFactory.h" class EnchantedMazeFactory : public MazeFactory { public: EnchantedMazeFactory(void); ~EnchantedMazeFactory(void); virtual IRoom* MakeRoom(int nRoomNo) const; virtual IDoor* MakeDoor(IRoom* pRoom1, IRoom* pRoom2) const; protected: Spell* CastSpell() const; }; #endif #include "EnchantedMazeFactory.h" #include "EnchantedRoom.h" #include "DoorNeedingSpell.h" EnchantedMazeFactory::EnchantedMazeFactory(void) { } EnchantedMazeFactory::~EnchantedMazeFactory(void) { } IRoom* EnchantedMazeFactory::MakeRoom(int nRoomNo) const { return new EnchantedRoom(nRoomNo, CastSpell()); } IDoor* EnchantedMazeFactory::MakeDoor(IRoom* pRoom1, IRoom* pRoom2) const { return new DoorNeedingSpell(pRoom1, pRoom2); } #ifndef IMAZE_H_ #define IMAZE_H_ class IRoom; class IMaze { public: IMaze(void); ~IMaze(void); void AddRoom(IRoom* pRoom); IRoom* RoomNo(int nNo) const; }; #endif #ifndef MAZE_GAME_H_ #define MAZE_GAME_H_ #include "MazeFactory.h" #include "IMaze.h" class MazeGame { public: MazeGame(void); ~MazeGame(void); IMaze* CreateMaze(MazeFactory& clsFactory); }; #endif #include "MazeGame.h" #include "IRoom.h" MazeGame::MazeGame(void) { } MazeGame::~MazeGame(void) { } IMaze* MazeGame::CreateMaze(MazeFactory& clsFactory) { IMaze* pMaze = clsFactory.MakeMaze(); IRoom* pRoom1 = clsFactory.MakeRoom(1); IRoom* pRoom2 = clsFactory.MakeRoom(2); IDoor* pDoor = clsFactory.MakeDoor(pRoom1, pRoom2); pMaze->AddRoom(pRoom1); pMaze->AddRoom(pRoom2); for (int nIndex = Direction_Start; nIndex < Direction_Max; nIndex++) { pRoom1->SetSide((RoomDirection)nIndex, (MapSite*)clsFactory.MakeWall()); pRoom2->SetSide((RoomDirection)nIndex, (MapSite*)clsFactory.MakeWall()); } pRoom1->SetSide(Direction_East, (MapSite*)pDoor); pRoom2->SetSide(Direction_West, (MapSite*)pDoor); return pMaze; } #ifndef SPELL_H_ #define SPELL_H_ class Spell { public: Spell(void); ~Spell(void); }; #endif #ifndef IDOOR_H_ #define IDOOR_H_ #include "MapSite.h" #include "IRoom.h" class IDoor : public MapSite { public: IDoor(IRoom* pRomm1, IRoom* pRoom2); ~IDoor(void); virtual void Enter(); IRoom* OtherSideFrom(IRoom* pRoom); private: IRoom* m_pRoom1; IRoom* m_pRoom2; }; #endif #ifndef IROOM_H_ #define IROOM_H_ #include "MapSite.h" enum RoomDirection { Direction_Start, Direction_North = Direction_Start, Direction_South, Direction_East, Direction_West, Direction_Max, }; class IRoom : public MapSite { public: IRoom(int nRoomNo); ~IRoom(void); MapSite* GetSide(RoomDirection eDire) const; void SetSide(RoomDirection eDire, MapSite* pSite); virtual void Enter(); private: std::map<RoomDirection,MapSite*> m_mapRootSite; int m_nRoomNo; }; #endif #ifndef IWALL_H_ #define IWALL_H_ #include "MapSite.h" class IWall : public MapSite { public: IWall(void); ~IWall(void); virtual void Enter(); }; #endif #ifndef MAP_SITE_H_ #define MAP_SITE_H_ class MapSite { public: MapSite(void); ~MapSite(void); virtual void Enter() = 0; }; #endif
-
相关模式
2.1.4 Builder(生成器)——对象创建型模式
- 意图
- 动机
- 适用性
- 结构
- 参与者
- 效果
- 实现
- 相关模式
2.1.5 Singleton(单例)——对象创建型模式
- 意图
- 动机
- 适用性
- 结构
- 参与者
- 效果
- 实现
- 相关模式
2.2 结构型模式
2.2.1 Adapter(适配器)——类对象结构型模式
-
意图
将一个类的接口转换成客户需要的另外一个接口。Adapter模式使得原本由于接口不兼容而不能在一起工作的类可以一起工作。
-
动机
有时为复用而设计的工具箱类不能够被复用,仅仅是因为它的接口与专业应用领域所需的接口不匹配。
-
适用性
- 你想使用一个已经存在的类,而它的接口不符合你的需求。
- 你想创建一个可以复用的类,该类可以与其他不相关的类或不可遇见的类(即使那些接口不兼容)协同工作。
- 你想使用一些已经存在的类,但是不可能对每个都进行子类化匹配它们的接口。对象适配器可以适配它的父类接口。
-
结构
-
类适配器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wnJKxjSq-1652451824234)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220507073321892.png)]
-
对象适配器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z0Iypba6-1652451824235)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220507073339053.png)]
-
-
参与者
- Target(参与者):定义Client使用的与特定领域相关的接口。
- Client(DrawingEditor):与符合Target接口的对象协同。
- Adaptee(TextView):定义一个已经存在的接口,这个接口需要适配。
- Adapter(TextShaPE):对Adaptee的接口与Target接口进行适配。
-
效果
类适配器的权衡:
- 用一个具体的Adapter类对Adaptee和Target进行适配。结果是想匹配一个类以及它的子类时,类Adaptor不能胜任工作。
- 使得Adapter可以重定义Adaptee的部分行为。
- 仅仅引入了一个对象,并不需要额外的指针间接获得Adaptee。
对象适配器的权衡:
- 允许一个Adaptor与多个Adaptee——Adaptee本身以及它的子类同时工作。
- 使得重定义Adaptee的行为比较困难。需要生成Adaptee的子类并且使得Adapter引用这个子类,而不是引用Adaptee本身。
-
实现
#ifndef _SHAPE_H_ #define _SHAPE_H_ #include <Gdiplus.h> class Manipulator; class Shape { public: Shape(void); ~Shape(void); virtual void BoundingBox(Point& clsBottomLeft, Point& clsTopRight); virtual Manipulator* CreateManipulator() const; }; #endif #ifndef _TEXT_VIEW_H_ #define _TEXT_VIEW_H_ #include <wincon.h> class TextView { public: TextView(void); ~TextView(void); void GetOrigin(COORD& clsX, COORD& clsY); void GetExtent(COORD& clsWidth, COORD& clsHeight); virtual bool IsEmpty() const; }; #endif #ifndef _TEXT_SHAPE_H_ #define _TEXT_SHAPE_H_ #include "Shape.h" #include "TextView.h" //通过多继承适配不同接口 class Manipulator; class TextShape : public Shape, public TextView { public: TextShape(void); ~TextShape(void); virtual void BoundingBox(Point& clsBottomLeft, Point& clsTopRight); virtual bool IsEmpty(); virtual Manipulator* CreateManipulator(); }; #endif #include "TextShape.h" TextShape::TextShape(void) { } TextShape::~TextShape(void) { } void TextShape::BoundingBox(Point& clsBottomLeft, Point& clsTopRight) { COORD clsBottom, clsLeft, clsWidth, clsHeight; GetOrigin(clsBottom, clsLeft); GetExtent(clsWidth, clsHeight); clsBottomLeft = Point(clsBottom, clsLeft); clsTopRight = Point(clsBottom + clsHeight, clsLeft + clsWidth); } bool TextShape::IsEmpty() { return TextView::IsEmpty(); } Manipulator* TextShape::CreateManipulator() { return new Manipulator(this); } #ifndef _TEXT_SHAPE_H_ #define _TEXT_SHAPE_H_ #include "Shape.h" //对象适配器 class TextShapeObj : public Shape { public: TextShapeObj(TextView* pTextView); ~TextShapeObj(void); virtual void BoundingBox(Point& clsBottomLeft, Point& clsTopRight); virtual bool IsEmpty(); virtual Manipulator* CreateManipulator(); private: TextView* m_pTextView; }; #endif #include "TextShapeObj.h" #include <lmcons.h> #include <assert.h> TextShapeObj::TextShapeObj(TextView* pTextView) : m_pTextView(pTextView) { } TextShapeObj::~TextShapeObj(void) { } void TextShapeObj::BoundingBox(Point& clsBottomLeft, Point& clsTopRight) { if (NULL == m_pTextView) { assert(false); return; } COORD clsBottom, clsLeft, clsWidth, clsHeight; m_pTextView->GetOrigin(clsBottom, clsLeft); m_pTextView->GetExtent(clsWidth, clsHeight); clsBottomLeft = Point(clsBottom, clsLeft); clsTopRight = Point(clsBottom + clsHeight, clsLeft + clsWidth); } bool TextShapeObj::IsEmpty() { if (NULL == m_pTextView) { assert(false); return; } return m_pTextView->IsEmpty(); } Manipulator* TextShapeObj::CreateManipulator() { return new Manipulator(this); }
-
相关模式
2.2.2 Bridge(桥接)——对象结构型模式
-
意图
将抽象部分与它的实现部分分离,使它们可以独立地变化(本质上来说该事务在多个维度变化)。
-
动机
当一个抽象有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口,而具体的子类则用不同方式加以实现。但是此方法有时不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分进行修改、扩充和复用。
-
适用性
- 你不希望抽象和它的实现部分之间有一个固定的绑定关系。
- 类的抽象以及它的实现都可以通过生成子类的方式进行扩展。
- 对一个抽象的实现部分修改,不应该对客户端代码产生影响。
- 你想对客户完全隐藏抽象的实现部分。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zg0p2KRL-1652451824235)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220509072217574.png)]
-
参与者
- Abstraction:定义抽象类的接口;维护一个指向Implementor类型对象的指针。
- RedefinedAbstraction:扩充由Abstraction定义的接口。
- Implementor:定义实现类的接口,该接口不一定要与Abstraction的接口一致,事实上可以完全不同。
- ConcreteImplementor:实现Implementor的接口,并定义它具体的实现。
-
效果
- 分离接口及其实现部分:一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时进行配置,一个对象甚至可以在运行时改变它的实现。
- 提高可扩充性,可以肚子地对Abstarction和Implementor层次结构进行扩充。
- 实现细节对客户的透明。
-
实现
#ifndef _WINDOW_H_ #define _WINDOW_H_ class WindowImp; class Window { public: Window(View* pContents); virtual ~Window(void); virtual void DrawContents(); virtual void Open(); virtual void Close(); virtual void Iconfy(); virtual void Deiconify(); virtual void SetOrigin(const Point& clsAt); virtual void SetExtent(const Point& clsExtent); virtual void Raise(); virtual void Lower(); virtual void DrawLine(const Point& clsPointStart, const Point& clsPointEnd); virtual void DrawRrect(const Point& clsPointTopLeft, const Point& clsPointRightBottom); virtual void DrawPlygon(const Point [] aPonts, int n); virtual void DrawText(const char* pChar, const Point& clsPoint); protected: WindowImp* GetWindowImp(); View* GetView(); private: WindowImp* m_pImp; View* m_pContents; }; #endif #include "Window.h" #include <lmcons.h> #include <assert.h> #include <Gdiplus.h> void Window::DrawRrect(const Point& clsPointTopLeft, const Point& clsPointRightBottom) { WindowImp* pImp = GetWindowImp(); pImp->ImpDeviceRect(clsPointTopLeft.X, clsPointTopLeft.Y, clsPointRightBottom.X, clsPointRightBottom.Y); } WindowImp* Window::GetWindowImp() { if (NULL == m_pImp) { m_pImp WindowSystemFactory::Instance()->MakeWindowImp(); } return m_pImp; } #ifndef _APPLICATION_WINDOW_H_ #define _APPLICATION_WINDOW_H_ #include "Window.h" class ApplicationWindow : public Window { public: ApplicationWindow(void); virtual ~ApplicationWindow(void); virtual void DrawContents(); }; #endif #include "ApplicationWindow.h" ApplicationWindow::ApplicationWindow(void) { } ApplicationWindow::~ApplicationWindow(void) { } void ApplicationWindow::DrawContents() { GetView()->DrawOn(this); } #ifndef _ICON_WINDOW_H_ #define _ICON_WINDOW_H_ #include "Window.h" class IconWindow : public Window { public: IconWindow(void); virtual ~IconWindow(void); virtual void DrawContents(); private: const char* m_pBitName; }; #endif #include "IconWindow.h" IconWindow::IconWindow(void) { } IconWindow::~IconWindow(void) { } void IconWindow::DrawContents() { WindowImp* pImp = GetWindowImp(); pImp->DeviceBitMap(m_pBitName, 0.0, 0.0); } #ifndef _WINDOW_IMP_H_ #define _WINDOW_IMP_H_ class WindowImp { public: virtual ~WindowImp(void); public: virtual void ImpTop() = 0; virtual void ImpBottom() = 0; virtual void ImpSetExtent() = 0; virtual void ImpSetOrigin() = 0; virtual void ImpDeviceRect() = 0; virtual void ImpDeviceText() = 0; virtual void ImpBitmap() = 0; virtual void ImpTop() = 0; virtual void ImpTop() = 0; virtual void ImpTop() = 0; protected: WindowImp(void); }; #endif #include "WindowImp.h" WindowImp::WindowImp(void) { } WindowImp::~WindowImp(void) { } #ifndef _XWINDOW_IMP_ #define _XWINDOW_IMP_ #include "WindowImp.h" class XWindowImp : public WindowImp { public: XWindowImp(void); ~XWindowImp(void); virtual void DeviceRect(COORD clsCoord1, COORD clsCoord2, COORD clsCoord3, COORD clsCoord4); private: Display* m_pDsp; Drawable m_clsWinid; GC m_gc; }; #endif #include "XWindowImp.h" #include <xcomplex> XWindowImp::XWindowImp(void) { } XWindowImp::~XWindowImp(void) { } void XWindowImp::DeviceRect(COORD clsCoord1, COORD clsCoord2, COORD clsCoord3, COORD clsCoord4) { int x = round(min(clsCoord1, clsCoord3)); int y = round(min(clsCoord2, clsCoord4)); int w = round(abs(clsCoord1-clsCoord3)); int h = round(abs(clsCoord2-clsCoord4)); XDrawRectangle(m_pDsp, m_clsWinid, m_gc, m_gc, x, y, w, h); } #ifndef _PM_WINDOW_IMP_ #define _PM_WINDOW_IMP_ #include "WindowImp.h" class PMWindowImp : public WindowImp { public: PMWindowImp(void); ~PMWindowImp(void); virtual void DeviceRect(COORD clsCoord1, COORD clsCoord2, COORD clsCoord3, COORD clsCoord4); private: HPS m_hps; }; #endif #include "PMWindowImp.h" #include <minmax.h> #include <wincon.h> PMWindowImp::PMWindowImp(void) { } PMWindowImp::~PMWindowImp(void) { } void PMWindowImp::DeviceRect(COORD clsCoord1, COORD clsCoord2, COORD clsCoord3, COORD clsCoord4) { COORD clsLeft = min(clsCoord1, clsCoord3); COORD clsRight = max(clsCoord2, clsCoord4); COORD clsBottom = abs(clsCoord1-clsCoord3); COORD clsTop = abs(clsCoord2-clsCoord4); PPOINTL point[4]; point[0].x = clsLeft; point[0].y = clsTop; point[1].x = clsRight; point[1].y = clsTop; point[2].x = clsRight; point[2].y = clsBottom; point[3].x = clsLeft; point[3].y = clsBottom; if ( (GpiBeginPath(m_hps, 1L) == false) || (GpiSetCurrentPosition(m_hps, &point[3]) == false) || (GpiPolyLine(m_hps, 4L, point) == GPI_ERROR) || (GpiEndPath(m_hps) == false) ) { //Report Error } else { GpiStrokePath(m_hps, 1L, 0L); } }
-
相关模式
2.2.3 Composite(组合)——对象结构型模式
-
意图
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
-
动机
在绘图编辑器和图形捕捉系统这样的图形应用系统中,用户可以使用简单的组件创建复杂的图表。用户可以组合多个简单组件组成以向较大的组件,这些组件又可以组合成更大的组件。
Composite模式的关键就是一个抽象类,它既可以表示图元,又可以表示图元的容器。
-
适用性
- 表示对象的部分-整体层次结构
- 用户忽略组合对象与单个对象的不同,用户将统一的使用组合结构中的所有对象。
-
结构
-
参与者
-
效果
-
实现
-
相关模式
2.2.4 Decorator(装饰)——对象结构型模式
- 意图
- 动机
- 适用性
- 结构
- 参与者
- 效果
- 实现
- 相关模式
2.2.5 Facade(外观)——对象结构型模式
- 意图
- 动机
- 适用性
- 结构
- 参与者
- 效果
- 实现
- 相关模式
2.2.6 Flyweight(享元)——对象结构型模式
- 意图
- 动机
- 适用性
- 结构
- 参与者
- 效果
- 实现
- 相关模式
2.2.7 Proxy(代理)——对象结构型模式
-
意图
-
动机
-
适用性
-
结构
-
参与者
-
效果
-
实现
-
相关模式
2.3 行为型模式
2.3.1 Chain of Responsibility(职责链)——对象行为模式
-
意图
处理发起请求和请求处理之间的逻辑。使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到一个对象处理它。
-
动机
考虑一个学生请假的场景,学生请假小于或等于2天,班主任可以批准,小于或等于7天,系主任可以批准;小于或等于10天,院长可以批准;其他情况不予批准。这个场景涉及到不同角色的对象对学生请假的请求进行处理,可以采用责任链模式解决。
-
适用性
- 有多个对象处理一个请求。
- 想在不明确指定对象接收者的情况下,向多个对象中的一个提交一个请求。
- 可处理一个请求的对象集合应被动态指定。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9bJ5ONY-1652451824236)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220416205459261.png)]
-
参与者
- Handler:定义一个处理请求的接口;实现后继链(可选)
- ConcreteHandler:处理它负责的请求;可访问它的继任者。如果可处理该请求就处理,否则将请求转发给它的继任者。
- Client:向链上的具体请求者对象提交请求。
-
效果
- 降低耦合度:是的请求的发起者无须知道是其中哪一个对象处理其请求。
- 增强了给对象指派职责的灵活性。
- 不保证一个请求一定能够被处理。
-
实现
//Leader.h #ifndef LEADER_H_ #define LEADER_H_ class Leader { public: Leader(Leader* pLeader); ~Leader(void); virtual void HandleRequest(int nLeaveDays); private: Leader* m_pLeader; }; #endif //Leader.cpp #include "Leader.h" Leader::Leader(Leader* pLeader) : m_pLeader(pLeader) { } Leader::~Leader(void) { } void Leader::HandleRequest(int nLeaveDays) { if (0 != m_pLeader) { //������������һ������ m_pLeader->HandleRequest(nLeaveDays); } } //ClassAdviser.h #ifndef CLASS_ADVISER_H_ #define CLASS_ADVISER_H_ #include "Leader.h" class ClassAdviser : public Leader { public: ClassAdviser(Leader* pLeader); ~ClassAdviser(void); virtual void HandleRequest(int nLeaveDays); }; #endif //ClassAdviser.cpp #include "ClassAdviser.h" ClassAdviser::ClassAdviser(Leader* pLeader) : Leader(pLeader) { } ClassAdviser::~ClassAdviser(void) { } void ClassAdviser::HandleRequest(int nLeaveDays) { if (2 >= nLeaveDays) { cout << "adviser permit the vocation!" << endl; } else { cout << "adviser can't permit the vocation! submit the request" << endl; Leader::HandleRequest(nLeaveDays); } } //DeanDepartment.h #ifndef DEPARTMENT_LEADER_H_ #define DEPARTMENT_LEADER_H_ #include "Leader.h" class DepartmentLeader : public Leader { public: DepartmentLeader(Leader* pLeader); ~DepartmentLeader(void); virtual void HandleRequest(int nLeaveDays); }; #endif //DeanDepartment.cpp #include "DepartmentLeader.h" DepartmentLeader::DepartmentLeader(Leader* pLeader) : Leader(pLeader) { } DepartmentLeader::~DepartmentLeader(void) { } void DepartmentLeader::HandleRequest(int nLeaveDays) { if (7 >= nLeaveDays) { cout << "Department Leader permit the vocation!" << endl; } else { cout << "Department Leader can't permit, submit the request!" << endl; Leader::HandleRequest(nLeaveDays); } } //Dean.h #ifndef DEAN_H_ #define DEAN_H_ #include "Leader.h" class Dean : public Leader { public: Dean(Leader* pLeader); ~Dean(void); virtual void HandleRequest(int nLeaveDays); }; #endif //Dean.cpp #include "Dean.h" Dean::Dean(Leader* pLeader) : Leader(pLeader) { } Dean::~Dean(void) { } void Dean::HandleRequest(int nLeaveDays) { if (10 >= nLeaveDays) { cout << "Dean permit the vocation!" << endl; } else { cout << "Dean can't permit, no one can handle the request!" << endl; } } //main.cpp #include "Leader.h" #include "Dean.h" #include "ClassAdviser.h" #include "DepartmentLeader.h" int main() { Leader* pDean = new Dean(0); Leader* pDepartmentLeader = new DepartmentLeader(pDean); Leader* pClassAdviser = new ClassAdviser(pDepartmentLeader); pClassAdviser->HandleRequest(8); return 0; }
-
相关模式
2.3.2 Command(命令)——对象行为模式
-
意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录清楚日志,以及支持可撤销的操作。
-
动机
用户界面工具箱包括按钮和菜单这样的对象,它们执行请求响应用户输入。但是工具箱不能显示的在按钮或者菜单中实现该请求,因为只有工具箱的应用代码知道该由哪个对象做哪个操作。
命令模式通过将请求本身变成一个对象来使工具对象可向未指定的应用对象提出请求。这个对象可以被存储,或者传递。这一模式的关键是一个抽象的command,它定义了执行操作的接口。
-
适用性
- 抽象出待执行的动作以参数化某对象。
- 在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。
- 支持取消操作。Command的Excute操作可在实施操作前将状态存储起来。
- 支持修改日志,这样当系统崩溃时,这些修改可以重做一遍。
- 用构建源语操作上的高层操作构造一个系统。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CTBT2Cjm-1652451824236)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220505225558262.png)]
-
参与者
- Command:声明执行操作的接口。
- ConcreteCommand:将一个接收者对象绑定一个动作;调用接收者相应的操作。
- Client:创建一个具体的命令并设置它的接收者。
- Invoker:要求该命名执行这个请求。
- Receiver:指定的如何实施与执行一个请求相关的操作。
-
效果
- 将调用操作的对象与知道如何实现该操作的对象解耦。
- Command是头等的对象,可以被操作和扩展。
- 可以将多个Command装配成一个组合命令。
- 增加新的Command很容易,因为无需改变已有的类。
-
代码示例
#ifndef _COMMAND_H_ #define _COMMAND_H_ class Command { public: virtual ~Command(void); virtual void Excute() = 0; protected: Command(void); }; #endif #ifndef _MACRO_COMMAND_H_ #define _MACRO_COMMAND_H_ #include "Command.h" #include <ios> #include <list> using namespace std; class MacroCommand : public Command { public: MacroCommand(void); virtual ~MacroCommand(void); virtual void Add(Command* pCommand); virtual void remove(Command* pCommand); virtual void Excute(); private: list<Command*> m_listCommand; }; #endif #include "MacroCommand.h" #include <ios> using namespace std; MacroCommand::MacroCommand(void) { } MacroCommand::~MacroCommand(void) { } void MacroCommand::Add(Command* pCommand) { m_listCommand.push_back(pCommand); } void MacroCommand::remove(Command* pCommand) { m_listCommand.remove(pCommand); } void MacroCommand::Excute() { std::list<Command*>::iterator itor = m_listCommand.begin(); for (; itor != m_listCommand.end(); ++itor) { (*itor)->Excute(); } } #ifndef _OPEN_COMMAND_H_ #define _OPEN_COMMAND_H_ #include "Command.h" class Application; class OpenCommand : public Command { public: OpenCommand(Application* pApplication); virtual ~OpenCommand(void); virtual void Excute(); protected: virtual const char* AskUser(); private: Application* m_pApplication; char* m_pCResponse; }; #endif #include "OpenCommand.h" #include <lmcons.h> class Document; OpenCommand::OpenCommand(Application* pApplication) : m_pApplication(pApplication) , m_pCResponse(NULL) { } //具体的命令对象借助应用代码实现具体操作 void OpenCommand::Excute() { const char* pName = AskUser(); if (NULL != pName) { Document* pDocumnet = new Document(pName); m_pApplication->Add(pDocumnet); pDocumnet->Open(); } } #ifndef _PASTE_COMMAND_H_ #define _PASTE_COMMAND_H_ #include "Command.h" class Document; class PasteCommand : public Command { public: PasteCommand(Document* pDocument); virtual ~PasteCommand(void); virtual void Excute(); private: Document* m_pDocumnet; }; #endif #include "PasteCommand.h" PasteCommand::PasteCommand(Document* pDocument) : m_pDocumnet(pDocument) { } void PasteCommand::Excute() { m_pDocumnet->Paste(); } #ifndef _SIMPLE_COMMAND_H_ #define _SIMPLE_COMMAND_H_ #include "Command.h" #include <lmcons.h> #include <assert.h> template<class Receiver> class SimpleCommand : public Command { public: typedef void (Receiver::*Action)(); SimpleCommand(Receiver* pReceiver, Action a); ~SimpleCommand(void); virtual void Excute(); private: Action m_Action; Receiver* m_pReceiver; }; template<class Receiver> void SimpleCommand<Receiver>::Excute() { if (NULL == m_pReceiver) { assert(false); return; } m_pReceiver->*m_Action(); } template<class Receiver> SimpleCommand<Receiver>::SimpleCommand(Receiver* pReceiver, Action a) : m_pReceiver(pReceiver) , m_Action(a) { } #endif
-
相关模式
2.3.3 Interpreter(解释器)——类行为模式
-
意图
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示解释语言中的句子。
-
动机
如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言的句子。这个样就可以构建一个解释器,该解释器通过解释这些句子解决该问题。例如,正则表达式是描述字符串模式的一种标准语言。与其为每一个模式都构造一个特定的算法,不如使用一种通用的搜索算法来解释执行一个正则表达式,该正则表达式定义了待匹配字符串的集合。
-
适用性
- 文法简单:对于复杂的文法,文法的类层次变大庞大而无法管理。
- 销售率不是关键问题。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FHMxYbqX-1652451824236)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220427073831067.png)]
-
参与者
- AbstractExpression(抽象表达式):声明一个抽象的解释操作,这个接口为抽象语法树的所有节点共享。
- TerminalExpression(终结符表达式):实现与文法中的终结符相关联的解释操作;一个句子中的每个操作符都需要该类的一个实例。
- NonterminalExpression:(非终结符表达式):对文法中的每一条规则。
- Context:(上下文):包含解释器之外的一些全局信息。
- Client:构建表示该文法的语言中一个特定句子的抽象语法树;使用解释操作。
-
效果
- 易于改变和扩展文法。
- 易于实现文法。
- 复杂的文法难以维护。
- 增加了新的解释表达式的方式。
-
代码示例
#ifndef BOOLEAN_EXP_H_ #define BOOLEAN_EXP_H_ #include"Context.h" class BooleanExp { public: BooleanExp(); virtual ~BooleanExp(); virtual bool Evaluate(Context& clsContext) = 0; virtual BooleanExp* Replace(const char* pChar, BooleanExp& clsExp) = 0; virtual BooleanExp* Copy() const = 0; }; #endif // !BOOLEAN_EXP_H_ #ifndef AND_EXP_H_ #define AND_EXP_H_ #include "BooleanExp.h" class AndExp :public BooleanExp { public: AndExp(BooleanExp* pExp, BooleanExp* pExp2); virtual ~AndExp(); virtual bool Evaluate(Context& clsContext); virtual BooleanExp* Replace(const char* pChar, BooleanExp& clsExp); virtual BooleanExp* Copy() const; private: BooleanExp* m_pOperate1; BooleanExp* m_pOperate2; }; #endif #include "AndExp.h" AndExp::AndExp(BooleanExp* pExp, BooleanExp* pExp2) : m_pOperate1(pExp) , m_pOperate2(pExp2) { } bool AndExp::Evaluate(Context& clsContext) { return m_pOperate1->Evaluate(clsContext) && m_pOperate2->Evaluate(clsContext); } BooleanExp* AndExp::Replace(const char* pChar, BooleanExp& clsExp) { return new AndExp(m_pOperate1->Replace(pChar, clsExp), m_pOperate2->Replace(pChar, clsExp)); } BooleanExp* AndExp::Copy() const { return new AndExp(m_pOperate1->Copy(), m_pOperate2->Copy()); } #ifndef CONTEXT_H_ #define CONTEXT_H_ #include "VariableExp.h" class Context { public: bool Lookup(const char* pChar) const; void Assign(VariableExp* pExp, bool bAssign); }; #endif // !CONTEXT_H_ #ifndef VARAIABLE_EXP_H_ #define VARAIABLE_EXP_H_ #include "BooleanExp.h" class VariableExp : public BooleanExp { public: VariableExp(const char* pExp); virtual ~VariableExp(); virtual bool Evaluate(Context& clsContext); virtual BooleanExp* Replace(const char* pChar, BooleanExp& clsExp); virtual BooleanExp* Copy() const; private: char* m_pCName; }; #endif #include "VariableExp.h" #include <string.h> VariableExp::VariableExp(const char* pExp) : m_pCName(const_cast<char*>(pExp)) { } bool VariableExp::Evaluate(Context& clsContext) { return clsContext.Lookup(m_pCName); } BooleanExp* VariableExp::Replace(const char* pChar, BooleanExp& clsExp) { if (strcmp(m_pCName, pChar) == 0) { return clsExp.Copy(); } else { return new VariableExp(m_pCName); } } BooleanExp* VariableExp::Copy() const { return new VariableExp(m_pCName); }
-
相关模式
2.3.4 Iterator(迭代器)——对象行为模式
-
意图
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
-
动机
一个聚合对象,如列表(ist),应该提供一种方法让别人可以访问它的元素,而又不需要暴露它的内部结构。此外,针对不同的需求,可能要以不同的方式遍历这个列表。
-
适用性
- 访问一个聚合对象的内容,而无需暴露它的内部表示。
- 支持聚合对象的多种遍历。
- 为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代)。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iquLouxG-1652451824237)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220426221553151.png)]
-
参与者
- Iterator(迭代器):迭代器定义访问和遍历元素的接口。
- ConcreteIterator(具体迭代器):具体迭代器实现迭代器接口;对该聚合遍历时跟着当前位置。
- Aggregate(聚合):聚合定义创建相应迭代器对象的接口。
- ConcreteAggregate(具体聚合):具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。
-
效果
- 支持以不同的方式遍历一个聚合。复杂的聚合可以用多种方式进行遍历。
- 简化了一个聚合的接口。有了迭代器的遍历接口,聚合本身就不再需要类似的遍历接口。
- 在同一个聚合上可以有多个遍历。每个迭代器保持它自己的遍历状态,因此可以同时进行多个遍历。
-
代码示例
#ifndef MY_LIST_H_ #define MY_LIST_H_ template<class Item> const INT32 DEFAULT_LIST_CAPACITY = 1000; class MyList { public: MyList(long size = DEFAULT_LIST_CAPACITY); long Count() const; Item& Get(long nIndex) const; ~MyList(void); }; #endif #ifndef MY_ITERATOR_H_ #define MY_ITERATOR_H_ template<class Item> class MyIterator { public: ~MyIterator(void); public: virtual void First() = 0; virtual void Next() = 0; virtual BOOL IsDone() const = 0; virtual Item CurrentItem() const = 0; protected: MyIterator(void); }; #endif #ifndef MY_LIST_ITERATOR_H_ #define MY_LIST_ITERATOR_H_ #include "MyIterator.h" #include <lmcons.h> #include <assert.h> template<class Item> class MyListIterator : public MyListIterator<Item> { public: MyListIterator(const MyList<Item>* pList); ~MyListIterator(void); virtual void First(); virtual void Next(); virtual void IsDone(); virtual Item CurrentItem() const; private: const MyList<Item>* m_pMyList; long m_nCurrentItemIndex; }; template<class Item> Item MyListIterator<Item>::CurrentItem() const { if (NULL == m_pMyList) { assert(false); return; } if (IsDone()) { return; } return m_pMyList->Get(m_nCurrentItemIndex) } template<class Item> void MyListIterator<Item>::IsDone() { if (NULL == m_pMyList) { assert(false); return; } return m_nCurrentItemIndex >= m_pMyList->Count(); } template<class Item> void MyListIterator<Item>::First() { m_nCurrentItemIndex = 0; } template<class Item> void MyListIterator<Item>::Next() { m_nCurrentItemIndex++; } template<class Item> MyListIterator<Item>::MyListIterator(const MyList<Item>* pList) : m_pMyList(pList) , m_nCurrentItemIndex(0) { } #endif
-
相关模式
2.3.5 Mediator(中介者)——对象行为模式
-
意图
用一个中介对象封装一系列对象的交互。中介者使各对象不需要显示引用,从而使其耦合松散。
-
动机
面向对象设计鼓励将行为分布到各个对象中。这种分布可能会导致对象间有许多连接。在最坏 的情况下,每个对象都知道其他所有对象。考虑一个图形界面对话框的实现。对话框的窗口组件之间存在依赖关系,例如当一个特定的输入为空时,某个按钮要禁用。因此即便对话框显示相同类型的窗口组件,也不能简单直接复用已有的窗口组件。可以将集体行为封装在一个单独的中介者对象中。中介者统一控制和协调一组对象的交互。
-
适用性
- 一组对象以定义良好但复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
- 一个对象引用其他很多对象且直接与这些对象通信。
- 想定义一个分散在多个类中的行为,又不想生成太多子类。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qhdj1g6T-1652451824237)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220425215322897.png)]
-
参与者
- Mediator:中介者定义一个接口用于各同事(Colleague)对象通信。
- ConcreteMediator:具体中介者通过协调各同事对象实现协作行为;了解并维护它的同事。
- Colleague class(同事类):每个同事都知道它的中介者对象;每一个同事对象在需要与其他同事通信的时候,与它的中介者通信。
-
效果
- 减少了子类生成:将原本分布于多个对象间的行为集中在一起。
- 将各Colleague解耦:Miediator有利于各Colleague间的松耦合,可以独立的改变和复用各collegue和media类。
- 简化了对象协议:用mediator和colleague之间的一对多交互,代替多对多交互。
- 对对象如何协作进行了抽象:将中介者作为一个独立的概念,并将其封装在一个对象中。
- 使控制集中话:中介者模式将交互的复杂性变为中介者的复杂性。
-
代码示例
//Widget.h #ifndef WIDGET_H_ #define WIDGET_H_ #include "DialogDirector.h" class Widget { public: Widget(DialogDirector* pDirector); ~Widget(void); virtual void Changed(); virtual void HandleMouse(MouseEvent& clsEvent); private: DialogDirector* m_pDirector; }; #endif //Widget.cpp #include "Widget.h" #include <lmcons.h> #include <assert.h> Widget::Widget(DialogDirector* pDirector) : m_pDirector(pDirector) { } Widget::~Widget(void) { } void Widget::Changed() { if (NULL == m_pDirector) { assert(false); return; } m_pDirector->WidgetChanged(this); } //IButton.h #ifndef IBUTTON_H_ #define IBUTTON_H_ #include "Widget.h" #include "DialogDirector.h" class IButton : public Widget { public: IButton(DialogDirector* pDirector); ~IButton(void); virtual void SetText(const char* pText); virtual void HandleMouse(MouseEvent& clsEvent); }; #endif //IButton.cpp #ifndef IBUTTON_H_ #define IBUTTON_H_ #include "Widget.h" #include "DialogDirector.h" class IButton : public Widget { public: IButton(DialogDirector* pDirector); ~IButton(void); virtual void SetText(const char* pText); virtual void HandleMouse(MouseEvent& clsEvent); }; #endif //IEntryFiled.h #ifndef IENTRY_FILED_H_ #define IENTRY_FILED_H_ #include "Widget.h" #include "DialogDirector.h" class IEntryFiled : public Widget { public: IEntryFiled(DialogDirector* pDirector); ~IEntryFiled(void); virtual void SetText(const char* pText); virtual const char* GetText(); virtual void HandleMouse(MouseEvent& clsEvent); }; #endif //IEntryFiled.cpp #include "IEntryFiled.h" IEntryFiled::IEntryFiled(DialogDirector* pDirector) :Widget(pDirector) { } IEntryFiled::~IEntryFiled(void) { } //IListBox.h #ifndef ILIST_BOX_H_ #define ILIST_BOX_H_ #include "Widget.h" #include "DialogDirector.h" #include <ios> using namespace std; class IListBox : public Widget { public: IListBox(DialogDirector* pDirector); ~IListBox(void); virtual const char* GetSelection(); virtual void SetList(std::List<char*>* pListItem); virtual void HandleMouse(MouseEvent& clsEvent); }; #endif //IListBox.cpp #include "IListBox.h" IListBox::IListBox(DialogDirector* pDirector) :Widget(pDirector) { } IListBox::~IListBox(void) { } //DiaglogDirector.h #ifndef DIALOG_DIRECTOR_H_ #define DIALOG_DIRECTOR_H_ class Widget; class DialogDirector { public: ~DialogDirector(void); virtual void ShowDialog(); virtual void WidgetChanged(Widget* pWidget) = 0; protected: DialogDirector(void); virtual void CreateWidgets() = 0; }; #endif //DialogDirector.cpp #include "DialogDirector.h" DialogDirector::DialogDirector(void) { } DialogDirector::~DialogDirector(void) { } //FontDialogDirector.h #ifndef FONT_DIALOG_DIRECTOR_H_ #define FONT_DIALOG_DIRECTOR_H_ #include "DialogDirector.h" #include "IButton.h" #include "IEntryFiled.h" #include "IListBox.h" class FontDialogDirector : public DialogDirector { public: FontDialogDirector(void); ~FontDialogDirector(void); virtual void WidgetChanged(Widget* pWidget); protected: virtual void CreateWidgets(); private: IButton* m_pOkBtn; IButton* m_pCancelBtn; IListBox* m_pFontBox; IEntryFiled* m_pFontName; }; #endif //FontDialogDirector.cpp #include "FontDialogDirector.h" #include <assert.h> FontDialogDirector::FontDialogDirector(void) { } FontDialogDirector::~FontDialogDirector(void) { } void FontDialogDirector::WidgetChanged(Widget* pWidget) { if ((NULL == m_pOkBtn) || (NULL == m_pCancelBtn) || (NULL == m_pFontBox) || (NULL == m_pFontName)) { assert(false); return; } if (pWidget == m_pFontBox) { m_pFontName->SetText(m_pFontBox->GetSelection()); } else if (pWidget == m_pOkBtn) { } else if (pWidget == m_pCancelBtn) { } } void FontDialogDirector::CreateWidgets() { m_pOkBtn = new IButton(this); m_pCancelBtn = new IButton(this); m_pFontBox = new IListBox(this); m_pFontName = new IEntryFiled(this); } //main.cpp #include <process.h> #include "FontDialogDirector.h" int main() { FontDialogDirector* pDiaglog = new FontDialogDirector(); system("pause"); return 0; }
-
相关模式
2.3.6 Memento(备忘录)——对象行为模式
-
意图
在不破坏封装的的前提下,捕获一个对象的内部状态,并在对象之外维护这个状态。
-
动机
有时必要记录一个对象的内部状态。为了允许用户取消不确定的操作或从错误中恢复过来,需要实现检查点和取消机制,而要实现这些机制,你必须事先将状态信息保存在某处,这样才能将对象恢复到它们先前的状态。但是对象通常封装了其部分或所有的状态信息,使得其状态不能被其他对象访问,也就不可能在该对象之外保存其状态。
-
适用性
- 必须保存一个对象在某个时刻的状态,这样以后需要时它才能恢复到先前的状态。
- 如果一个接口让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jO8FuAsM-1652451824238)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220424224128604.png)]
-
参与者
- Memento:备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态;防止原发器以外的其他对象访问备忘录。备忘录有两个接口,管理者Caretacker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。原发器可以看到一个宽接口,允许它访问返回到之前的状态。
- Originator:创建一个备忘录,用以记录当前时刻的状态;使用备忘录的内容进行操作。
- Caretaker:负责保存好备忘录;不能对备忘录的内容进行操作或检查。
-
效果
- 保持封装便界:使用备忘录可以避免暴露一些只应有原发器管理却又存储在原发器之外的信息。
- 简化了原发器
- 使用备忘录可能代价很高:除非封装和恢复Originator状态的开销不大,否则该模式并不合适。
- 定义宽接口和窄接口。
-
代码示例
-
相关模式
2.3.7 Observer(观察者)——对象行为模式
-
意图
定义对象间的一种一对多的依赖,当一个对象的状态改变时,所有依赖于它的对象都得到通知并被自动更新。
-
动机
对于许多图形用户界面工具箱将用户应用的界面表示与底下的应用数据分离。例如,一个表格对象和一个柱状图可使用不同的表示形式描述同一个应用数据对象的信息。表格对象和柱状图对象相互不知道对方的存在,这样使你可以根据需要单独复用表格或柱状图。但是数据变化后,表格对象和柱状图对象都要根据数据刷新显示。
-
适用性
- 一个抽象模型有两个方面,其中一个方面依赖于另外一个方面。
- 对一个对象的改变需要同时改变其他对象,而不知道有多少这样的对象有待改变。
- 一个对象必须通知其他对象,而它又不能假定其他对象是谁。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VvrlXzxS-1652451824238)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220420225602763.png)]
-
参与者
- Subject:目标知道它的观察者;提供注册和删除观察者的接口。
- Observer:提供刷新接口
- ConcreteSubject:将有关状态存入各Observer对象
- ConcreteServer:维护一个指向ConcreteSubject的引用;存储有关状态;实现更新接口。
-
效果
- 目标和管着者间的抽象耦合,一个对象仅知道它有一系列观察者,每个都是符合抽象类的简单借接口。
- 支持广播通信。Subject变了,所有Observer收到通知。
- 意外更新,观察者之前相互不知道,可能相互影响。
-
代码示例
//Observer.h #ifndef OBSERVER_H_ #define OBSERVER_H_ class Subject; class Observer { public: ~Observer(void); virtual void Update(Subject* pSubject) = 0; protected: Observer(void); }; #endif //DigitalClock.h #ifndef DIGITAL_CLOCK_H_ #define DIGITAL_CLOCK_H_ #include "Observer.h" #include "ClockTimer.h" #include "IWidget.h" class DigtalClock : public IWidget, public Observer { public: DigtalClock(ClockTimer* pClockTimer); ~DigtalClock(void); virtual void Update(Subject* pSubject); virtual void Draw(); private: ClockTimer* m_pSubject; }; #endif //DigitalClock.pp #include "DigtalClock.h" #include <assert.h> #include <lmcons.h> DigtalClock::DigtalClock(ClockTimer* pClockTimer) : m_pSubject(pClockTimer) { m_pSubject->Attach(this); } DigtalClock::~DigtalClock(void) { m_pSubject->Detach(this); } void DigtalClock::Update(Subject* pSubject) { if (NULL == pSubject || NULL == m_pSubject) { assert(false); return; } if (m_pSubject == pSubject) { Draw(); } } void DigtalClock::Draw() { if (NULL == m_pSubject) { assert(false); return; } int hour = m_pSubject->GetHour(); int minute = m_pSubject->GetMinute(); //..... } //AnalogClock.h #ifndef ANALOG_CLOCK_H_ #define ANALOG_CLOCK_H_ #include "Observer.h" #include "ClockTimer.h" #include "IWidget.h" class AnalogClock : public IWidget, public Observer { public: AnalogClock(ClockTimer* pClockTimer); ~AnalogClock(void); virtual void Update(Subject* pSubject); virtual void Draw(); private: ClockTimer* m_pSubject; }; #endif //AnalogClock.cpp #include "AnalogClock.h" #include <assert.h> #include <lmcons.h> AnalogClock::AnalogClock(ClockTimer* pClockTimer) : m_pSubject(pClockTimer) { m_pSubject->Attach(this); } AnalogClock::~AnalogClock(void) { m_pSubject->Detach(this); } void AnalogClock::Update(Subject* pSubject) { if (NULL == pSubject || NULL == m_pSubject) { assert(false); return; } if (m_pSubject == pSubject) { Draw(); } } void AnalogClock::Draw() { if (NULL == m_pSubject) { assert(false); return; } int hour = m_pSubject->GetHour(); int minute = m_pSubject->GetMinute(); //..... } //Subject.h #ifndef SUBJECT_H_ #define SUBJECT_H_ #include "Observer.h" #include <ios> #include <list> class Subject { public: ~Subject(void); virtual void Attach(Observer* pObserver); virtual void Detach(Observer* pObserver); virtual void Notify(); protected: Subject(void); private: std::list<Observer*> m_listObserver; }; #endif //Subject.cpp #include "Subject.h" #include <lmcons.h> #include <assert.h> #include "Observer.h" Subject::Subject(void) { } Subject::~Subject(void) { } void Subject::Attach(Observer* pObserver) { if (NULL == pObserver) { assert(false); return; } m_listObserver.push_back(pObserver); } void Subject::Detach(Observer* pObserver) { if (NULL == pObserver) { assert(false); return; } m_listObserver.remove(pObserver); } void Subject::Notify() { std::list<Observer*>::iterator itor = m_listObserver.begin(); for (; itor != m_listObserver.end(); ++itor) { (*itor)->Update(this); } } //ClockTimer.h #ifndef CLOCK_TIMER_H_ #define CLOCK_TIMER_H_ #include "Subject.h" class ClockTimer : public Subject { public: ClockTimer(void); ~ClockTimer(void); virtual int GetHour(){return 0;}; virtual int GetMinute(){return 0;}; virtual int GetSecond(){return 0;}; void Tick(); }; #endif //ClockTimer.cpp #include "ClockTimer.h" ClockTimer::ClockTimer(void) { } ClockTimer::~ClockTimer(void) { } void ClockTimer::Tick() { Subject::Notify(); } //IWieget.h #ifndef IWIDGET_H_ #define IWIDGET_H_ class IWidget { public: IWidget(void); ~IWidget(void); virtual void Draw(); }; #endif //IWidget.cp #include "IWidget.h" IWidget::IWidget(void) { } IWidget::~IWidget(void) { } void IWidget::Draw() { } //main.cpp #include "ClockTimer.h" #include "AnalogClock.h" #include "DigtalClock.h" int main() { ClockTimer* pTimer = new ClockTimer; AnalogClock* pAngleClock = new AnalogClock(pTimer); DigtalClock* pDigitalClock = new DigtalClock(pTimer); system("pause"); return 0; }
-
相关模式
2.3.8 State(状态)——对象行为模式
-
意图
允许一个对象在其内部状态改变时改变它的行为。
-
动机
考虑一个表示网络连接的类TCPConnection。一个网络连接会有:连接已建立、正在监听、连接已关闭。当一个TCPConnection对象收到其他对象的请求时,根据自身的当前状态做出不同的反应。状态模式为不同的状态提供一个抽象类,表示网络的连接状态。TCPState类为各表示不同的操作状态的子类声明了一个公共接口。TCPState子类实现与特定转态相关的行为。
-
适用性
- 一个对象的行为取决于它的状态,并且必须在运行时根据状态改变它的行为。
- 一个操作中含有庞大的分支的条件语句,且这些分支依赖于该对象的状态。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-maZxi15k-1652451824238)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220418223705908.png)]
-
参与者
- Context(环境,如TCPConnection):定义客户感兴趣的接口;维护一个ConreteState的实例。
- State:定义一个处理接口。
- ConreteState:根据状态实现对应接口。
-
效果
- 将与特定状态相关的行为局部化,并且将不同的行为分割开。
- 使得状态转换显示化。State对象可保证Context对象不会发生内部状态不一致的情况,因为从ontext的角度来看,转换是原子的。
-
代码示例
//TCPState.h #ifndef TCP_STATE_H_ #define TCP_STATE_H_ class TCPConnection; class TCPOctetStream; class TCPState { public: TCPState(); ~TCPState(); virtual void Transmit(TCPConnection* pConnection, TCPOctetStream* pOctetStream){}; virtual void ActiveOpen(TCPConnection* pCOnnection){}; virtual void PassiveOpen(TCPConnection* pConnection){}; virtual void Close(TCPConnection* pConnection){}; virtual void Synchronize(TCPConnection* pConnection){}; virtual void Acknowledge(TCPConnection* pConnection){}; virtual void Send(TCPConnection* pConnection){}; protected: void ChangeState(TCPConnection* pConnection, TCPState* pState); }; #endif //TcpState.cpp #include "TCPState.h" #include "TCPConnection.h" TCPState::TCPState(/* args */) { } TCPState::~TCPState() { } void TCPState::ChangeState(TCPConnection* pConnection, TCPState* pState) { if (0 != pConnection || 0 != pState) { pConnection->ChangeState(pState); } } //TCPClose.h #ifndef TCP_CLOSED_H_ #define TCP_CLOSED_H_ #include "TCPState.h" class TCPClosed : public TCPState { public: TCPClosed(/* args */); ~TCPClosed(); virtual void ActiveOpen(TCPConnection* pConnection); virtual void PassiveOpen(TCPConnection* pConnection); }; #endif //TCPClose.cpp #include "TCPClosed.h" #include "TCPConnection.h" #include "TCPEstablished.h" #include "TCPListen.h" TCPClosed::TCPClosed(/* args */) { } TCPClosed::~TCPClosed() { } void TCPClosed::ActiveOpen(TCPConnection* pConnection) { if (0 != pConnection) { /* code */ TCPEstablished* pEstablishend = new TCPEstablished(); ChangeState(pConnection, pEstablishend); } } void TCPClosed::PassiveOpen(TCPConnection* pConnection) { if (0 != pConnection) { /* code */ TCPListen* pListen = new TCPListen(); ChangeState(pConnection, pListen); } } //TCPConnection.h #ifndef TCP_CONNECTION_H_ #define TCP_CONNECTION_H_ class TCPOctetStream; class TCPState; class TCPConnection { public: TCPConnection(/* args */); ~TCPConnection(); void ActiveOpen(); void PassiveOpen(); void Close(); void Send(); void Ackonwledge(); void Synchronize(); void ProcessOctet(TCPOctetStream* pOctetStream); private: friend class TCPState; void ChangeState(TCPState* pState); private: TCPState* m_pState; }; #endif //TCPConnection.cpp #include "TCPConnection.h" #include "TCPState.h" #include "TCPClosed.h" TCPConnection::TCPConnection() { m_pState = new TCPClosed(); } TCPConnection::~TCPConnection() { } void TCPConnection::ChangeState(TCPState *pState) { if (0 != m_pState) { /* code */ delete m_pState; m_pState = 0; } if (0 != pState ) { m_pState = pState; } } void TCPConnection::ActiveOpen() { if (0 != m_pState) { /* code */ m_pState->ActiveOpen(this); } } void TCPConnection::PassiveOpen() { if (0 != m_pState) { /* code */ m_pState->PassiveOpen(this); } } void TCPConnection::Close() { if (0 != m_pState) { /* code */ m_pState->Close(this); } } void TCPConnection::Send() { if (0 != m_pState) { /* code */ m_pState->Send(this); } } void TCPConnection::Ackonwledge() { if (0 != m_pState) { /* code */ m_pState->Acknowledge(this); } } void TCPConnection::Synchronize() { if (0 != m_pState) { /* code */ m_pState->Synchronize(this); } } void TCPConnection::ProcessOctet(TCPOctetStream* pOctetStream) { } //TCPEstablished.h #ifndef TCP_ESTABLISHED_H_ #define TCP_ESTABLISHED_H_ #include "TCPState.h" class TCPEstablished : public TCPState { public: TCPEstablished(/* args */); ~TCPEstablished(); virtual void Transmit(TCPConnection* pConnection, TCPOctetStream* pOctetStream); virtual void Close(TCPConnection* pConnection); }; #endif //TCPEstablished.cpp #include "TCPEstablished.h" #include "TCPListen.h" TCPEstablished::TCPEstablished(/* args */) { } TCPEstablished::~TCPEstablished() { } void TCPEstablished::Transmit(TCPConnection* pConnection, TCPOctetStream* pOctetStream) { } void TCPEstablished::Close(TCPConnection* pConnection) { TCPListen* pListen = new TCPListen(); ChangeState(pConnection, pListen); } //TCPListen.h #ifndef TCP_LISTEN_H_ #define TCP_LISTEN_H_ #include "TCPState.h" class TCPListen : public TCPState { public: TCPListen(/* args */); ~ TCPListen(); virtual void Send(TCPConnection* pConnection); }; #endif //TCPLisen.cpp #include "TCPListen.h" #include "TCPConnection.h" #include "TCPEstablished.h" TCPListen::TCPListen() { } TCPListen::~TCPListen() { } void TCPListen::Send(TCPConnection* pConnection) { if (0 != pConnection) { /* code */ TCPEstablished* pEstablished = new TCPEstablished(); ChangeState(pConnection, pEstablished); } } //main.cpp #include "TCPConnection.h" #include <stdio.h> int main() { TCPConnection* pConnection = new TCPConnection(); pConnection->ActiveOpen(); pConnection->Send(); return 0; }
-
相关模式
2.3.9 Strategy(策略)——对象行为模式
-
意图
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可以独立于它的客户而变化,隔离算法变化与客户端代码。
-
动机
- 问题场景:超市购物系统中,用于结算的策略会经常变化。将这些结算的方法与客户端代码进行耦合是不可取的。假设该系统已经开发完成一期上线了,后面新增了几种用于结算的算法。
- 解决策略:使用该模式能很好的遵守开闭原则,在对在线系统影响最新的情况下完成系统的迭代。
-
适用性
- 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不同变体。例如:超市结算策略。
- 算法使用客户不应该知道的数据。
- 一个类定义了多种行为,并且这些多个行为在这个类的操作中以多个条件语句的形式出现。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uEYCy2r8-1652451824239)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220408073806848.png)]
-
参与者
- Strategy:定义所有算法的公共接口。Context使用这个接口调用某ConcreteStrategy定义的算法。
- ConcreteStrategy:具体的算法
- Context:用一个concreteStrategy对象配置;维护一个对Strategy对象的引用;可定义一个接口让Strategy访问它的数据。
-
效果
- 相关算法系列:Strategy类层次为Context定义了一系列的可供复用的算法或行为。
- 消除了一些条件语句:是的客户端代码无需使用条件语句,实现不同的策略。
-
代码示例
//CashSuper.h #ifndef STRATEGY_H_ #define STRATEGY_H_ class CashSuper { public: CashSuper(); ~CashSuper(); virtual double AcceptCash(double fMoney) = 0; }; class CashNormal : public CashSuper { public: public: CashNormal(); ~CashNormal(); virtual double AcceptCash(double fMoney) { return fMoney; }; }; class CashRebate : public CashSuper { public: CashRebate(double fMoenyRebate); ~CashRebate(); virtual double AcceptCash(double fMoney) { return fMoney * m_fMoneyRebate; }; private: double m_fMoneyRebate; }; class CashReturn : public CashSuper { public: CashReturn(double fMoneyConditon, double fMoneyReturn); ~CashReturn(); virtual double AcceptCash(double fMoney); private: double m_fMoneyCondition; double m_fMoneyReturn; }; #endif //CashSuper.cpp #include "CashSuper.h" CashSuper::CashSuper() { } CashSuper::~CashSuper() { } CashNormal::CashNormal() { } CashNormal::~CashNormal() { } CashRebate::CashRebate(double fMoenyRebate) : m_fMoneyRebate(fMoenyRebate) { } CashRebate::~CashRebate() { } CashReturn::CashReturn(double fMoneyConditon, double fMoneyReturn) : m_fMoneyCondition(fMoneyConditon) , m_fMoneyReturn(fMoneyReturn) { } CashReturn::~CashReturn() { } double CashReturn::AcceptCash(double fMoney) { double fResultMoney = fMoney; if (fMoney >= m_fMoneyCondition) { fResultMoney = fMoney - m_fMoneyReturn; } return fResultMoney; } //Context.h #ifndef CASH_CONTEXT_H_ #define CASH_CONTEXT_H_ class CashSuper; enum CashType { CashType_Start, CashType_Normal = CashType_Start, CashType_Rebate, CashType_Return, CashType_Max, }; class CashContext { public: CashContext(CashType eCashType); ~CashContext(); public: double GetResultMoney(double fMoney); private: void CreateCashAlgorithm(CashType eType); private: CashSuper* m_pCashSuper; }; #endif //Context.cpp #include "CashContext.h" #include "CashSuper.h" CashContext::CashContext(CashType eCashType) : m_pCashSuper(0) { CreateCashAlgorithm(eCashType); } CashContext::~CashContext() { if (0 != m_pCashSuper) { delete m_pCashSuper; m_pCashSuper = 0; } } double CashContext::GetResultMoney(double fMoney) { if (0 == m_pCashSuper) { return 0; } return m_pCashSuper->AcceptCash(fMoney); } void CashContext::CreateCashAlgorithm(CashType eType) { switch (eType) { case CashType_Normal: m_pCashSuper = new CashNormal(); break; case CashType_Rebate: m_pCashSuper = new CashRebate(0.8); break; case CashType_Return: m_pCashSuper = new CashReturn(549.4, 12.54); break; default: break; } } //客户端代码 #include "CashContext.h" #include "CashSuper.h" #include <iostream> using namespace std; int main() { CashContext* pCashContext = new CashContext(CashType_Rebate); double fPrice = 6867896.98; cout << "根据当前策略,本次实际收钱:" << pCashContext->GetResultMoney(fPrice) << endl; return 0; }
-
相关模式
2.3.10 Template Method(模板方法)——类行为模式
-
意图
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。在子类中根据需要,重定义该算法的某些特定步骤。
-
动机
- 问题场景:考虑一个提供Application和Document类的应用框架。Application负责打开一个文档(可以是任何形式的文档),文档打开后,由一个Document对象表示。打开文档的操作会因文档类型的不同,而有所区别(Word、Excel)。
- 解决策略:定义一个OpenDocument操作,明确了打开文档的每一个步骤。检查该文档是否能被打开,创建与应用相关的Document对象,将其加入文档集合中,并从一个文档中读取该Document。在不同类型的文档中,可以重定义特定步骤。
-
适用性
- 一次性实现一个算法的不变部分,并可变的特定步骤可以留给子类实现。
- 将各子类中的公共方法被提取出来并集合到一个公共父类中避免代码重复。
- 控制子类扩展。模板方法只在特定的点调用钩子操作(在派生类实现的操作)。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1v9I3otI-1652451824239)(C:\Users\15497\AppData\Roaming\Typora\typora-user-images\image-20220413230255265.png)]
-
参与者
- AbstractClass(抽象类):定义抽象的原子操作,具体的子类将重定义它们,以实现一个算法的特定步骤。
- 实现一个模板方法,定义算法的骨架。该模板方法不仅调用原子操作,也调用定义在AbstractClass或其他对象中的操作。
-
效果
模板方法是一种代码复用的基本技术。它们在类库中尤为重要,提取类库中的公共行为。
模板方法调用下列类型的操作:
- 具体的操作(ConcreteClass或客户类的操作)
- 具体的AbstractClass操作。
- 原子操作。
- FactoryMethod。
- 钩子操作,它们提供了缺省的操作,子类可以在必要时进行扩展。
-
代码示例
//LayoutBase.h #ifndef LAYOUT_BASE_H_ #define LAYOUT_BASE_H_ class LayoutBase { public: LayoutBase(); ~LayoutBase(); public: //TemplateMethod void UpdateLayout(); //两个钩子操作 virtual void CreateLeftLayout() {}; virtual void CreateRightLayout() {}; }; #endif //LayoutBase.cpp #include "LayoutBase.h" #include <iostream> using namespace std; LayoutBase::LayoutBase() { } LayoutBase::~LayoutBase() { } void LayoutBase::UpdateLayout() { CreateLeftLayout(); CreateRightLayout(); cout << "Combine left layout and right layuout! << finish layout" << endl; } //LayoutPad.h #ifndef LAYOUT_PAD_H_ #define LAYOUT_PAD_H_ #include "LayoutBase.h" class LayoutPad : public LayoutBase { public: LayoutPad(); ~LayoutPad(); protected: virtual void CreateLeftLayout(); virtual void CreateRightLayout(); }; #endif //LayoutPad.cpp #include "LayoutPad.h" #include "LayoutPad.h" #include <iostream> using namespace std; LayoutPad::LayoutPad() { } LayoutPad::~LayoutPad() { } void LayoutPad::CreateLeftLayout() { cout << "CreateLeftLayout in pad!" << endl; } void LayoutPad::CreateRightLayout() { cout << "CreateRightLayout in pad!" << endl; } //LayoutPhone.h #ifndef LAYOUT_PHONE_H_ #define LAYOUT_PHONE_H_ #include "LayoutBase.h" class LayoutPhone : public LayoutBase { public: LayoutPhone(); ~LayoutPhone(); protected: virtual void CreateLeftLayout(); virtual void CreateRightLayout(); }; #endif //LayoutPhone.cpp #include "LayoutPhone.h" #include <iostream> using namespace std; LayoutPhone::LayoutPhone() { } LayoutPhone::~LayoutPhone() { } void LayoutPhone::CreateLeftLayout() { cout << "CreateLeftLayout in phone!" << endl; } void LayoutPhone::CreateRightLayout() { cout << "CreateLeftLayout in phone!" << endl; } //main.cpp #include <iostream> #include "LayoutPad.h" #include "LayoutPhone.h" #include "LayoutBase.h" using namespace std; int main() { //在pad客户端代码 LayoutBase* pLayout = new LayoutPad(); pLayout->UpdateLayout(); //在phone客户端 LayoutBase* pLayout = new LayoutPhone(); pLayout->UpdateLayout(); return 0; }
-
相关模式
2.3.11 Visitor(访问者模式)——对象行为模式
-
意图
表示作用于某对象结构中各元素的操作。使你可以在不改变各元素类的前提下,定义作用于这些元素的新操作。
-
动机
- 问题场景:在软件构建过程中,软件已经开发完成,但是由于需求变更,某些类层次结构中需要增加新的行为。如果直接在基类中做这样的更改,将会给子类带来繁重的负担,甚至破坏原有设计。
- 解决策略:该模式可以在不改变类层次结构的前提下,在运行时根据需要透明的为类层次结构上的各个类动态增加新的操作。
-
适用性
在下列情况下使用Visitor模式:
- 一个对象结构包含很多类对象,他们有不同的接口,而你想对这些对象实施一些依赖具体类的操作。
- 定义对象结构的类很少改变,但需要经常在此结构上定义新的操作。
- 需要对一个对象结构中的对象进行很多不同且不相关的操作,而你想避免让这些操作污染这些类对象。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MaRc1W0u-1652451824244)()]
图2.1 访问者模式结构图 -
参与者
- Visitor:为该对象结构中的每一个类声明一个Visit操作。是的访问者可以通过该元素的特定接口直接访问它。
- ConcreteVisitor:实现每个由Visitor声明的操作。该算法片段是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文,并存储它的局部状态。
- Element:定义一个Accept接口,它以访问者为参数。
- ConcreteElement:实现Accept接口,该操作以访问者为接口。
- ObjectStructure:能枚举它的元素;可以提供一个高层的接口允许该访问者访问它的元素;可以是一个组合或是一个集合。
-
效果
- 访问者模式是的易于增加新的操作。
- 访问者集中相关的操作而分离无关的操作。
- 增加新的ConcreteElement类很困难。
- 访问对象结构中的每一个元素时,可以累积状态。
-
代码示例
//visitor.h #ifndef VISITOR_H_ #define VISITOR_H_ #include "Man.h" #include "Woman.h" class Man; class Woman; class Visitor { public: Visitor(void); virtual ~Visitor(void); public: virtual void VisitMan(Man* pMan) = 0; virtual void VisitWoman(Woman* pWoman) = 0; }; #endif //HairStyleVisitor.h #ifndef HAIR_STYLE_VISITOR_H_ #define HAIR_STYLE_VISITOR_H_ #include "Visitor.h" class HairStyleVisitor : public Visitor { public: HairStyleVisitor(void); virtual ~HairStyleVisitor(void); public: virtual void VisitMan(Man* pMan); virtual void VisitWoman(Woman* pWoman); }; #endif //HairStyleVisitor.cpp #include "HairStyleVisitor.h" #include <ios> #include <ostream> #include <iostream> using namespace std; HairStyleVisitor::HairStyleVisitor(void) { } HairStyleVisitor::~HairStyleVisitor(void) { } void HairStyleVisitor::VisitMan(Man* pMan) { cout << "Man Like Short hair more!" << endl; } void HairStyleVisitor::VisitWoman(Woman* pWoman) { cout << "Woman Like Long hair more!" << endl; } //HealthVisitor.h #ifndef HEALTH_VISITOR_H_ #define HEALTH_VISITOR_H_ #include "Visitor.h" class HealthVisitor : public Visitor { public: HealthVisitor(void); virtual ~HealthVisitor(void); public: virtual void VisitMan(Man* pMan); virtual void VisitWoman(Woman* pWoman); }; #endif //HealthVisitor.cpp #include "HealthVisitor.h" #include <ios> #include <ostream> #include <iostream> using namespace std; HealthVisitor::HealthVisitor(void) { } HealthVisitor::~HealthVisitor(void) { } void HealthVisitor::VisitMan(Man* pMan) { cout << "Man focus on male sick!" << endl; } void HealthVisitor::VisitWoman(Woman* pWoman) { cout << "Woman focus on femal sick!" << endl; } //Person.h #ifndef PERSON_H_ #define PERSON_H_ class Visitor; class Person { public: Person(void); virtual ~Person(void); public: virtual void Accept(Visitor* pVisitor) = 0; }; #endif //Man.h #ifndef MAN_H_ #define MAN_H_ #include "Visitor.h" #include "Person.h" class Person; class Visitor; class Man : public Person { public: Man(void); virtual ~Man(void); public: virtual void Accept(Visitor* pVisitor); }; #endif //Man.cpp #include "Man.h" #include <assert.h> #include <lmcons.h> Man::Man(void) { } Man::~Man(void) { } void Man::Accept(Visitor* pVisitor) { if (NULL == pVisitor) { assert(false); return; } pVisitor->VisitMan(this); } //Woman.h #ifndef WOMAN_H_ #define WOMAN_H_ #include "Person.h" #include "Visitor.h" class Person; class Woman : public Person { public: Woman(void); virtual ~Woman(void); public: virtual void Accept(Visitor* pVisitor); }; #endif //Woman.cpp #include "Woman.h" #include <lmcons.h> #include <assert.h> Woman::Woman(void) { } Woman::~Woman(void) { } void Woman::Accept(Visitor* pVisitor) { if (NULL == pVisitor) { assert(false); return; } pVisitor->VisitWoman(this); } //Collegue.h #ifndef COLLEGUE_H_ #define COLLEGUE_H_ #include "Visitor.h" #include "Person.h" #include <ios> #include <vector> class Collegue { public: public: Collegue(void); ~Collegue(void); public: void Accept(Visitor* pVisitor); void AddCollegue(Person* pPerson); void RemoveCollegue(Person* pPerson); private: std::vector<Person*> m_vecCollegue; }; #endif //Collegue.cpp #include "Collegue.h" #include "Visitor.h" #include <assert.h> #include <lmcons.h> Collegue::Collegue(void) { } Collegue::~Collegue(void) { } void Collegue::Accept(Visitor* pVisitor) { if (NULL == pVisitor) { assert(false); return; } std::vector<Person*>::iterator itor = m_vecCollegue.begin(); for (; itor != m_vecCollegue.end(); ++itor) { (*itor)->Accept(pVisitor); } } void Collegue::AddCollegue(Person* pPerson) { if (NULL == pPerson) { assert(false); return; } m_vecCollegue.push_back(pPerson); } void Collegue::RemoveCollegue(Person* pPerson) { if (NULL == pPerson) { assert(false); return; } std::vector<Person*>::iterator itor = m_vecCollegue.begin(); while(itor != m_vecCollegue.end()) { if (*itor == pPerson) { break;; } ++itor; } m_vecCollegue.erase(itor); } //Client.cpp(客户端代码) #include "HairStyleVisitor.h" #include "HealthVisitor.h" #include "Man.h" #include "Person.h" #include "Visitor.h" #include "Woman.h" using namespace std; enum CollegueNo { Collegue_Start, CollegueNo_1 = Collegue_Start, CollegueNo_2 , CollegueNo_3 , CollegueNo_4 , CollegueNo_Max, }; enum CollegueGender { CollegueGender_Start, CollegueGender_Man = CollegueGender_Start, CollegueGender_Woman, CollegueGender_Max, }; struct CollegueInfo { public: CollegueNo m_eCollegueNo; CollegueGender m_eCollegueGender; }; static CollegueInfo sCollegueInfos [] = { {CollegueNo_1, CollegueGender_Man}, {CollegueNo_2, CollegueGender_Woman}, {CollegueNo_3, CollegueGender_Man}, {CollegueNo_4, CollegueGender_Woman}, }; enum CollegueInfoParser { CollegueInfoParser_Start, CollegueInfoParser_HairStyle = CollegueInfoParser_Start, CollegueInfoParser_Health, CollegueInfoParser_Max, }; void InitCollegueInfo(std::vector<Person*>& vecCollegue) { vecCollegue.resize(Collegue::CollegueNo_Max); for (int nIndex = 0 ; nIndex < ARRAYSIZE(sCollegueInfos); nIndex++) { Person* pPerson = NULL; if (sCollegueInfos[nIndex].m_eCollegueGender == Collegue::CollegueGender_Woman) { pPerson = new Woman(); } else { pPerson = new Man(); } vecCollegue.push_back(pPerson); } } void ReleaseCollegueInfo(std::vector<Person*>& vecCollegues) { if (vecCollegues.empty()) { return; } std::vector<Person*>::iterator itor = vecCollegues.begin(); for (; itor != vecCollegues.end(); ++itor) { delete (*itor); (*itor) = NULL; } } int main(void) { std::vector<Person*> vecCollegues; InitCollegueInfo(vecCollegues); Collegue* pCollegue = new Collegue(); std::vector<Person*>::iterator itor = vecCollegues.begin(); for (; itor != vecCollegues.end(); ++itor) { pCollegue->AddCollegue(*itor); } std::map<CollegueInfoParser, Visitor*> mapCollegueInfoProcessor; mapCollegueInfoProcessor[CollegueInfoParser_HairStyle] = new HairStyleVisitor(); mapCollegueInfoProcessor[CollegueInfoParser_Health] = new HealthVisitor(); pCollegue->Accept(mapCollegueInfoProcessor[CollegueInfoParser_HairStyle]); pCollegue->Accept(mapCollegueInfoProcessor[CollegueInfoParser_Health]); ReleaseCollegueInfo(vecCollegues); delete mapCollegueInfoProcessor[CollegueInfoParser_HairStyle]; mapCollegueInfoProcessor[CollegueInfoParser_HairStyle] = NULL; delete mapCollegueInfoProcessor[CollegueInfoParser_Health]; mapCollegueInfoProcessor[CollegueInfoParser_Health] = NULL; return 0; }
-
相关模式
3、Q&&A
3.1 对象(创建/结构/行为)模式和类(创建/结构/行为)行为模式的区别
类在编译的时候可以确定的差异,对象在运行时明确。
3.2 黑盒复用、白盒复用概念解释
白箱复用:通过生成子类的复用。
黑箱复用:通过组装或对象组合的复用。
3.3 如何描述一个设计模式
- 意图:设计模式是做什么的?它的基本原理和意图?它解决什么样的特定设计问题?
- 动机:基于一个设计问题,说明特定情景下,如何用模式中的类和对象解决该问题。
- 适用性:什么情况下使用该模式?该模式可用来进行哪些不良改进?怎么识别这些结构?
- 结构:UML图形描述。
- 参与者:模式涉及的类、对象的各自职责。
- 协作:模式参与者如何协作实现它们的职责。
- 效果:模式怎样支持它的目标?使用模式的效果和所需做的权衡是什么?系统结构哪些方面可以独立改变?
- 代码示例:基于C++实现的代码示例。
- 相关模式:与这个模式紧密相关的模式有哪些?其间重要的不同之处是什么?这个模式应与哪些模式一起使用?