注:本文主要结合他人blog总结形成,没有系统进行理论学习,肯能很多观点与设计模式本来吃思想有出入。代码都经过编译验证,源代码:https://git.oschina.net/evan-xia/x_1603_DesignPattern.git
零、设计模式
0.1、创建型
0.2、结构型
0.3、行为型
15. Chain of Responsibility(责任链)
0.4、补充
1、之间的关系
2、设计模式的六大原则
A、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
B、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
C、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
D、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
E、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
F、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
一、创建型
1.1、Factory Method(工厂方法)
1、意图:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
2、角色:
A、Product:抽象产品
B、ConcreteProduct:具体产品
C、Factory:抽象工厂
D、ConcreteFactory:具体工厂
3、适用性:
A、当一个类不知道它所必须创建的对象的类的时候。
B、当一个类希望由它的子类来指定它所创建的对象的时候。
C、当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
4、C++实现:
#include <iostream> using namespace std; //工厂方法抽象的对象接口; class Product { }; //具体对象实现类; class ConcreteProduct:public Product { }; //创建器接口,返回Product对象; class Creator { public: virtual Product* FactoryMethod()=0; }; //实现Creator,返回ConcreteProduct对象; class ConcreteCreator:public Creator { public: Product* FactoryMethod() { cout<<"Product created!"<<endl; return new ConcreteProduct; } }; int main() { Creator* creator=new ConcreteCreator(); Product* product=creator->FactoryMethod(); return 0; } //---------输出--------- Product created!
5、补充:
A、上面是GOF对FactoryMethod的阐述,字里行间,都流露出继承思想:父类(Creator)不知道他所必须创建的类的对象(Product),于是就留了个接口,委托给子类(ConcreteCreator)来实现(重写)。
B、Creator类中还有个AnOperation方法,GOF还专门为这个方法做了注释,该方法是一个Template Method(模板方法),其调用FactoryMethod()。并且在相关模式中,GOF也重复提到:“工厂方法通常在Template Method中被调用”,很多介绍Factory Method的例子,都直接无视这个Method和注释,导致误用Factory Method模式。
C、需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
1.2、Abstract Factory(抽象工厂)
1、意图:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
2、角色:
A、AbstractFactory:抽象工厂
B、ConcreteFactory:具体工厂
C、AbstractProduct:抽象产品
D、Product:具体产品
3、适用性:
A、一个系统要独立于它的产品的创建、组合和表示时。
B、一个系统要由多个产品系列中的一个来配置时。
C、当你要强调一系列相关的产品对象的设计以便进行联合使用时。
D、当你提供一个产品类库,而只想显示它们的接口而不是实现时。
4、C++实现:
#include "iostream" using namespace std; class AbstractProductA { }; class ProductA1:public AbstractProductA { }; class ProductA2:public AbstractProductA { }; class AbstractProductB { }; class ProductB1:public AbstractProductB { }; class ProductB2:public AbstractProductB { }; //声明一个抽象产品对象的操作接口; class AbstractFactory { public: virtual AbstractProductA* CreateProductA()=0; virtual AbstractProductB* CreateProductB()=0; }; //实现创建对象的具体操作; class ConcreteFactroy1:public AbstractFactory { public: AbstractProductA* CreateProductA() { cout<<"ProductA1 created!"<<endl; return new ProductA1; } AbstractProductB* CreateProductB() { cout<<"ProductB1 created!"<<endl; return new ProductB1; } }; //实现创建对象的具体操作; class ConcreteFactroy2:public AbstractFactory { public: AbstractProductA* CreateProductA() { cout<<"ProductA2 created!"<<endl; return new ProductA2; } AbstractProductB* CreateProductB() { cout<<"ProductB2 created!"<<endl; return new ProductB2; } }; int main() { AbstractFactory* abs1=new ConcreteFactroy1(); AbstractFactory* abs2=new ConcreteFactroy2(); AbstractProductA* productA1=abs1->CreateProductA(); AbstractProductB* productB1=abs1->CreateProductB(); AbstractProductA* productA2=abs2->CreateProductA(); AbstractProductB* productB2=abs2->CreateProductB(); return 0; } //---------输出--------- ProductA1 created! ProductB1 created! ProductA2 created! ProductB2 created!
5、补充:
A、与Factory Method区别:
a、Abstract Factory模式主要通过工厂类的不同的子类来实现不同类别的产品的创建。而Factory Method模式主要通过工厂类的方法的多态来实现对多种不同产品的创建。
b、对象职责上:Abstract Factory中的Factory,只具有创建对象(一个产品系列)的唯一职责;而Factory Method中的Creator,具有实际的逻辑和意义。
c、扩展上:Abstract Factory侧重水平扩展,而Factory Method侧重垂直扩展。
d、使用上:Abstract Factory的思想是聚合,而Factory Method的思想是继承。
B、产品族难扩展,产品等级易扩展。
1.3、Builder(建造者)
1、意图:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2、角色:
A、Builder:抽象建造者
B、ConcreteBuilder:具体建造者
C、Director:指挥者
D、Product:产品角色
3、适用性:
A、当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
B、当构造过程必须允许被构造的对象有不同的表示时。
4、C++实现:
#include "iostream" using namespace std; //对象产品; class Product { }; //创建对象的抽象接口; class Builder { public: virtual void BuildPartA()=0; virtual void BuildPartB()=0; }; //实现Builder接口并构建对象产品的各个部件; class ConcreteBuilder:public Builder { public: void BuildPartA() { cout<<"BuildPartA"<<endl; } void BuildPartB() { cout<<"BuildPartB"<<endl; } Product* GetResult() { cout<<"Build Completed!"<<endl; return new Product; } }; //构建一个使用Builder的接口; class Director { private: Builder* m_builder; public: Director(Builder* builder) { m_builder=builder; } void Construct() { m_builder->BuildPartA(); m_builder->BuildPartB(); } }; int main() { ConcreteBuilder* builder=new ConcreteBuilder(); Director* director=new Director(builder); director->Construct(); Product* product=builder->GetResult(); return 0; } //---------输出--------- BuildPartA BuildPartB Build Completed!
5、补充:
A、Builder模式的关键在于,将“要做什么”与“做出来”分离,将“如何装配”与“完成装配”分离:Director知道“要做什么”, Builder负责“做出来”;Director负责指挥, Builder负责实施;
B、与Abstract Factory区别:Builder模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类——导演类。
a、工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;
b、建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。
c、一般来说,如果产品的建造很复杂,那么请用工厂模式;如果产品的建造更复杂,那么请用建造者模式。
1.4、Prototype(原型)
1、意图:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
2、角色:
A、Prototype:声明克隆自身的接口;
B、ConcretePrototype:实现克隆自身的操作;
3、适用性:
A、当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者
B、为了避免创建一个与产品类层次平行的工厂类层次时;或者
C、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
4、C++ 实现:
#include <iostream> using namespace std; class Prototype { private: int num; char* name; public: virtual Prototype* clone()=0; virtual Prototype* deepclone()=0; virtual void sayHello()=0; }; class ConcretePrototype:public Prototype { private: int num; char* name; public: Prototype* clone() { return new ConcretePrototype(this->name,this->num); } Prototype* deepclone() { int tempnum; char* tempname=new char[strlen(name)+1]; tempnum=num; strcpy(tempname,name); return new ConcretePrototype(tempname,tempnum); } void sayHello() { cout<<"ConcrePrototype:"<<this->name<<":"<<this->num<<endl; } ConcretePrototype(char* na,int n) { name=na; num=n; }; }; int main() { Prototype* pPrototype1=new ConcretePrototype("conan",100); Prototype* pPrototype11=pPrototype1->clone(); pPrototype1->sayHello(); pPrototype11->sayHello(); Prototype* pPrototype2=new ConcretePrototype("柯南",200); Prototype* pPrototype22=pPrototype2->deepclone(); pPrototype2->sayHello(); pPrototype22->sayHello(); return 0; } //---------输出--------- ConcrePrototype:conan:100 ConcrePrototype:conan:100 ConcrePrototype:柯南:200 ConcrePrototype:柯南:200
5、补充:
A、注意深拷贝和浅拷贝;
a、浅复制:如果字段是值类型的(包括string),则对该字段执行逐位复制,如字段是引用类型,则复制引用但不自制引用的对象。因此,原始对象及其复本引用同一对象。通过Object的MemberwiseClone()方法即可实现。
b、深复制:如果字段是值类型的,则对该字段执行逐位复制,如字段是引用类型,则复制引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。让子类也继承Prototype角色几口实现Clone()方法,使用逐层克隆赋值引用类型字段的办法完成。
B、Prototype 模式允许一个物件再创建另外一个可定制的物件,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型物件传给那个要发动创建的物件,这个要发动创建的物件通过请求原型物件拷贝它们自己来实施创建。
1.5、Singleton(单例)
1、意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2、角色:
A、Singleton:单例
3、适用性:
A、当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
B、当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
4、C++实现:
#include <iostream> using namespace std; class Singleton { private: static Singleton* uniqueInstance; int num; Singleton() //私有化构造函数 { } public: static Singleton* Instance() { if(!uniqueInstance) { cout<<"new uniqueInstance"<<endl; //Lock(); //借用其它类来实现,如boost uniqueInstance = new Singleton(); uniqueInstance->num = 0; //Unlock(); } return uniqueInstance; } void increaseNum() { this->uniqueInstance->num ++; } void printNum() { cout<<this->uniqueInstance->num<<endl; } ~Singleton() { if(Singleton::uniqueInstance) delete Singleton::uniqueInstance; } }; Singleton* Singleton::uniqueInstance=NULL; //初始化,不加会报错 int main() { Singleton* sa = Singleton::Instance(); Singleton* sb = Singleton::Instance(); sa->printNum(); sb->printNum(); sa->increaseNum(); sa->printNum(); sb->printNum(); return 0; } //---------输出--------- new uniqueInstance 0 0 1 1
5、补充:
A、单例模式注意:
a、私有化构造函数
b、 定义静态的Singleton uniqueInstance对象和Instance()方法
c、多线程环境下,Instance()方法中需要使用同步锁synchronized (Singleton.class)防止多线程同时进入造成uniqueInstance被多次实例化
二、结构型
2.1、Adapter Class/Object(适配器)
1、意图:
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
2、角色:
A、Target:目标抽象类
B、Adapter:适配器类
C、Adaptee:适配者类
D、Client:客户类
3、适用性:
A、你想使用一个已经存在的类,而它的接口不符合你的需求。
B、你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
C、(仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
4、C++实现:
#include <iostream> using namespace std; //目标接口 class Target { public: virtual void Request() = 0; }; //用于对象适配,像C#,JAVA等不支持多继承可用这种方式 class Obj_Adaptee { public: void SpecificRequest() { cout<<"Obj_Adaptee SpecificRequest"<<endl; } }; class Obj_Adapter:public Target { private: Obj_Adaptee objadaptee; public: void Request() { objadaptee.SpecificRequest(); } }; //用于类适配 class Cls_Adaptee { public: void SpecificRequest() { cout<<"Cls_Adaptee SpecificRequest"<<endl; } }; class Cls_Adapter:public Target,private Cls_Adaptee { void Request() { SpecificRequest(); } }; int main(int argc, char const *argv[]) { //对象适配 Target *objtarget = new Obj_Adapter(); objtarget->Request(); //类适配 Target *clstarget = new Cls_Adapter(); clstarget->Request(); return 0; } //---------输出--------- Obj_Adaptee SpecificRequest Cls_Adaptee SpecificRequest
5、补充:
A、一般而言,适配器不是在详细设计师添加的,而是解决正在服役的项目的问题;
2.2、Bridge(桥接)
1、意图:
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
2、角色:
A、Abstraction:抽象类
B、RefinedAbstraction:扩充抽象类
C、Implementor:实现类接口
D、ConcreteImplementor:具体实现类
3、适用性:
A、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
B、设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。客户的代码不必重新编译。
C、一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。
D、虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
E、你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
F、类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时Bridge 模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
4、C++实现:
#include <iostream> using namespace std; //实现类接口; class Implementor { public: virtual void Operation()=0; }; //具体实现类; class ConcreteImplementorA:public Implementor { public: void Operation() { cout<<"ConcreteImplementorA"<<endl; } }; //具体实现类; class ConcreteImplementorB:public Implementor { public: void Operation() { cout<<"ConcreteImplementorB"<<endl; } }; //抽象类接口; class Abstraction { private: Implementor *imp; public: Abstraction(Implementor *imp) { this->imp = imp; } void Operation() { imp->Operation(); } }; //扩展Abstraction的接口; class RefinedAbstraction:public Abstraction { public: void Operation() { } }; int main(int argc, char const *argv[]) { Implementor* ia = new ConcreteImplementorA(); Implementor* ib = new ConcreteImplementorB(); Abstraction* aa = new Abstraction(ia); Abstraction* ab = new Abstraction(ib); aa->Operation(); ab->Operation(); return 0; } //---------输出--------- ConcreteImplementorA ConcreteImplementorB
5、补充:
A、【GOF95】在提出桥梁模式的时候指出,桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化"。这句话有三个关键词,也就是抽象化、实现化和脱耦。
2.3、Composite(组合)
1、意图:
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。
2、角色:
A、Component:顶层接口,用于访问子组件;
B、Leaf:叶节点,没有子节点;
C、Composite:用于存储子节点;
3、适用性:
A、你想表示对象的部分-整体层次结构。
B、你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
4、C++实现:
#include <iostream> #include <stdlib.h> #include <list> using namespace std; //顶层接口,用于访问子组件; class Component { public: virtual void Operation()=0; virtual void Add(Component *component) {} virtual void Remove(Component *component) {} virtual Component* GetChild(int nIndex) { return NULL; } }; //叶节点,没有子节点; class Leaf:public Component { public: void Operation() { cout<<"This is the Leaf!"<<endl; } }; //用于存储子节点; class Composite:public Component { private: std::list<Component*> children; public: ~Composite() { std::list<Component*>::iterator iter1,iter2,tmp; for(iter1=children.begin(),iter2=children.end() ; iter1!=iter2; ++iter1) { tmp = iter1; delete (*tmp); } } void Operation() { cout << "Operation by Composite"<<std::endl; std::list<Component*>::iterator iter1, iter2; for(iter1=children.begin(), iter2 = children.end();iter1 != iter2;++iter1) (*iter1)->Operation(); } void Add(Component *component) { children.push_back(component); } void Remove(Component *component) { std::list<Component*>::iterator iter; iter=std::find(children.begin(),children.end(),component); if (children.end()!=iter) children.erase(iter); } Component* GetChild(int nIndex) { if (nIndex <= 0 || nIndex > children.size()) return NULL; std::list<Component*>::iterator iter1,iter2; int i; for (i=1,iter1=children.begin(),iter2=children.end(); iter1!=iter2; ++iter1,++i) { if (i == nIndex) break; } return *iter1; } }; int main(int argc, char const *argv[]) { Leaf* leaf1 = new Leaf(); Leaf* leaf2 = new Leaf(); Component* component = new Composite(); component->Add(leaf1); component->Add(leaf2); component->Operation(); cout<<endl; component->GetChild(1)->Operation(); cout<<endl; component->Remove(leaf1); component->Operation(); return 0; } //---------输出--------- Operation by Composite This is the Leaf! This is the Leaf! This is the Leaf! Operation by Composite This is the Leaf!
5、补充:
A、组合模式的实现根据所实现接口的区别分为两种形式,分别称为安全模式和透明模式。组合模式可以不提供父对象的管理方法,但组合模式必须在合适的地方提供子对象的管理方法(诸如:add、remove、getChild等)。
B、组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以向处理简单元素一样来处理复杂元素。如果你想要创建层次结构,并可以在其中以相同的方式对待所有元素,那么组合模式就是最理想的选择。
2.4、Decorator(装饰)
1、意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。
2、角色:
A、Component: 抽象构件
B、ConcreteComponent: 具体构件
C、Decorator: 抽象装饰类
D、ConcreteDecorator: 具体装饰类
3、适用性:
A、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
B、处理那些可以撤消的职责。
C、当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
4、C++实现:
#include <iostream> using namespace std; //对象接口,可以给这些对象动态添加功能; class Component { public: virtual void Operation() = 0; }; //具体对象; class ConcreteComponent:public Component { public: void Operation() { cout<<"ConcreteComponent Operation!"<<endl; } }; //修饰接口; class Decorator:public Component { private: Component* component; public: Decorator(Component* component) { this->component = component; } void Operation() { component->Operation(); } }; //向组件添加功能;; class ConcreteDecoratorA:public Decorator { private: int addState; public: ConcreteDecoratorA(Component* pComponent):Decorator(pComponent) {} void Operation() { Decorator::Operation(); cout<<"ConcreteDecoratorA Operation!"<<endl; } }; //向组件添加功能; class ConcreteDecoratorB:public Decorator { public: ConcreteDecoratorB(Component* pComponent):Decorator(pComponent) {} void Operation() { Decorator::Operation(); AddedBehavior(); } void AddedBehavior() { cout<<"ConcreteDecoratorB AddedBehavior!"<<endl; } }; int main(int argc, char const *argv[]) { Component* component = new ConcreteComponent(); component->Operation(); cout<<endl; //添加功能 Decorator* decoratorA = new ConcreteDecoratorA(component); decoratorA->Operation(); cout<<endl; //再添加功能 Decorator* decoratorB = new ConcreteDecoratorB(decoratorA); decoratorB->Operation(); cout<<endl; return 0; } //---------输出-------- ConcreteComponent Operation! ConcreteComponent Operation! ConcreteDecoratorA Operation! ConcreteComponent Operation! ConcreteDecoratorA Operation! ConcreteDecoratorB AddedBehavior!
5、补充:
A、与其他模式
a、Adapter模式:Decorator模式不同于Adapter模式,因为装饰仅改变对象的职责而不改变它的接口;而适配器将给对象一个全新的接口。
b、Composite模式:可以将装饰视为一个退化的、仅有一个组件的组合。然而,装饰仅给对象添加一些额外的职责—它的目的不在于对象聚集。
c、Strategy模式:用一个装饰你可以改变对象的外表;而Strategy模式使得你可以改变对象的内核。这是改变对象的两种途径。
B、组建完成基本功能,为了扩展别的功能,将组建嵌入到另一个对象里面由另一个对象完成相应的任务。
2.5、Facade(外观)
1、意图:
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
2、角色:
A、Facade: 外观角色
B、SubSystem:子系统角色
3、适用性:
A、当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
B、客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
C、当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。
4、C++实现:
#include <iostream> using namespace std; //系统类 class SubSysOne { public: void MethodOne() { cout<<"MethodOne"<<endl; } }; class SubSysTwo { public: void MethodTwo() { cout<<"MethodTwo"<<endl; } }; class SubSysThree { public: void MethodThree() { cout<<"MethodThree"<<endl; } }; //外观类 class Facade { private: SubSysOne* sub1; SubSysTwo* sub2; SubSysThree* sub3; public: Facade() { sub1 = new SubSysOne(); sub2 = new SubSysTwo(); sub3 = new SubSysThree(); } ~Facade() { delete sub1; delete sub2; delete sub3; } void FacadeMethod() { sub1->MethodOne(); sub2->MethodTwo(); sub3->MethodThree(); } }; //客户端 int main() { Facade* test = new Facade(); test->FacadeMethod(); return 0; } //--------输出--------- MethodOne MethodTwo MethodThree
5、补充:
A、Facade设计模式更注重从架构的层次去看整个系统,而不是单个类的层次。Facade很多时候更是一种架构设计模式。
B、Facade更注重简化接口,Adapter模式注重转换接口,Bridge模式注重分离接口(抽象)与其实现,Decorator模式注重稳定接口的前提下为对象扩展功能。
2.6、Flyweight(享元)
1、意图:
运用共享技术有效地支持大量细粒度的对象。
2、角色:
A、Flyweight: 抽象享元类
C、ConcreteFlyweight: 具体享元类
D、UnsharedConcreteFlyweight: 非共享具体享元类
E、FlyweightFactory: 享元工厂类
3、适用性:
A、一个应用程序使用了大量的对象。
B、完全由于使用大量的对象,造成很大的存储开销。
C、对象的大多数状态都可变为外部状态。
D、如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
E、应用程序不依赖于对象标识。由于Flyweight 对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
4、C++代码:
#include <iostream> #include <vector> #include <string> using namespace std; //享元类 class Flyweight { private: string _intrinsicState; protected: Flyweight(string intrinsicState) { this->_intrinsicState = intrinsicState; } public: virtual ~Flyweight() {} virtual void Operation(const string& extrinsicState) {} string GetIntrinsicState() { return this->_intrinsicState; } }; //具体享元类 class ConcreteFlyweight:public Flyweight { public: ConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState) { cout<<"ConcreteFlyweight Build....."<<intrinsicState<<endl; } ~ConcreteFlyweight() {} //实现接口 void Operation(const string& extrinsicState) { cout<<"内部["<<this->GetIntrinsicState()<<"] 外部["<<extrinsicState<<"]"<<endl; } }; //享元工厂 class FlyweightFactory { private: vector<Flyweight*> _fly; public: FlyweightFactory() {} ~FlyweightFactory() {} //确保合理的共享 Flyweight Flyweight* GetFlyweight(const string& key) { vector<Flyweight*>::iterator it = _fly.begin(); for (; it != _fly.end(); it++) { if ((*it)->GetIntrinsicState() == key) { cout<<"already created by users...."<<endl; return *it; } } Flyweight* fn = new ConcreteFlyweight(key); _fly.push_back(fn); return fn; } }; //测试 int main(int argc,char* argv[]) { FlyweightFactory* fc = new FlyweightFactory(); //不同的对象,享元工厂将会创建新的享元类 Flyweight* fw1 = fc->GetFlyweight("Object A"); Flyweight* fw2 = fc->GetFlyweight("Object B"); //相同的对象,享元工厂将会使用一个已创建的享元类 Flyweight* fw3 = fc->GetFlyweight("Object A"); fw1->Operation("fw1"); fw2->Operation("fw2"); fw3->Operation("fw3"); return 0; } //---------输出--------- ConcreteFlyweight Build.....Object A ConcreteFlyweight Build.....Object B already created by users.... 内部[Object A] 外部[fw1] 内部[Object B] 外部[fw2] 内部[Object A] 外部[fw3]
5、补充:
A、享元工厂类是重点,因为它创建并管理享元对象,对没有的对象它会创建,对已有的对象它会提供一个已创建的实例。
B、可以想像有一个对象池,里面都是一些享元类,享元工厂的作用就是从对象池里取对象。
C、它的目的是大幅度地减少需要实例化的类的数量。
D、注意:注意划分外部状态和内部状态,否则可能会引起线程安全问题。 而且这些类必须有一个工厂对象加以控制。
2.7、Proxy(代理)
1、意图:
为其他对象提供一种代理以控制对这个对象的访问。即给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
2、角色:
A、Subject: 抽象主题角色
B、Proxy: 代理主题角色
C、RealSubject: 真实主题角色
3、适用性:
A、远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地 的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在 另一台主机中,远程代理又叫做大使(Ambassador)。
B、虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
C、Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟 到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个 开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
D、保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
E、防火墙(Firewall)代理:保护目标不让恶意用户接近。
同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
F、智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。
4、C++实现:
#include <iostream> using namespace std; //抽象角色 :声明真实对象和代理对象的共同接口; class Subject { public: virtual void Request()=0; }; //真实角色 :代理角色所代表的真实对象,是我们最终要引用的对象。 class RealSubject: public Subject { public: void Request() { cout<<"RealSubject Request"<<endl; } }; //代理角色 : // 代理对象角色 内部含有对真实对象的引用,从而可以操作真实对象 // 同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象 // 同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。 class Proxy: public Subject { private: RealSubject* realsubject; public: Proxy() { realsubject=NULL; } void Request() { if(!realsubject) realsubject = new RealSubject(); cout<<"Proxy Request-->"; realsubject->Request(); } }; int main(int argc, char const *argv[]) { Subject* reals = new RealSubject(); reals->Request(); Subject* proxys = new Proxy(); proxys->Request(); return 0; } //--------输出--------- RealSubject Request Proxy Request-->RealSubject Request
5、补充:
A、日常开发过程中的使用场景还是很多的。比如调用WebService,我们总会自动或手动生成客户端的代理类,这是远程代理模式;对于登录,也总可以使用保护代理和智能引用代理。
B、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
C、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
三、行为型
3.1、Interpreter(解释器)
1、意图:
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
2、角色:
A、AbstractExpression: 抽象表达式。声明一个抽象的解释操作,该接口为抽象语法树中所有的节点共享。
B、TerminalExpression: 终结符表达式。实现与文法中的终结符相关的解释操作。实现抽象表达式中所要求的方法。文法中每一个终结符都有一个具体的终结表达式与之相对应。
C、NonterminalExpression: 非终结符表达式。为文法中的非终结符相关的解释操作。
D、Context: 环境类。包含解释器之外的一些全局信息。
E、Client: 客户类。
3、适用性:
A、当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
B、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
C、一些重复出现的问题可以用一种简单的语言来进行表达。
D、文法较为简单。
4、C++实现:
#include <iostream> #include <string> using namespace std; //需要解释的文本内容 class Context { private: string input; string output; public: Context() { input = ""; output = ""; } }; //抽象解释器,具有统一的解释器接口 class AbstractExpression { public: void virtual interpret(Context *context)=0; }; //具体的解释器1,方法interpret中应该是对Context的解释。。 class TerminalExpression : public AbstractExpression { public: void interpret(Context *context) { cout << "Terminal Expression --> context" << endl; } }; //具体解释器2 class NonterminalExpression : public AbstractExpression { public: void interpret(Context *context) { cout << "Non-Terminal Expression --> context" << endl; } }; int main() { Context *context = new Context(); AbstractExpression *t = new TerminalExpression(); AbstractExpression *n = new NonterminalExpression(); t->interpret(context); n->interpret(context); return 0; } //---------输出--------- Terminal Expression --> context Non-Terminal Expression --> context
5、补充:
A、解释器模式就是用“迷你语言”来表现程序要解决的问题。比如:在C语言解释器,当你输入 int 时,解释器就能正确的开辟一个 int 的空间出来。再比如: linux 下常用的命令参数,如 ls -a,-a 就能被正确的解释成相应的命令。
B、优点:这种模式很容易改变和扩展文法,因为每个文法有一个文法类,也就是上面的表达式类。
C、缺点:当文法非常复杂时,要管理和维护很多个文法类。
3.2、Template Method(模板方法)
1、意图:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
2、角色:
A、AbstractClass:抽象类,定义抽象的原语操作。
B、ConcreteClass:具体实现类。
3、适用性:
A、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
B、各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke 和Johnson 所描述过的“重分解以一般化”的一个很好的例子[ OJ93 ]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
C、控制子类扩展。模板方法只在特定点调用“hook ”操作(参见效果一节),这样就只允许在这些点进行扩展。
4、C++代码:
#include <iostream> using namespace std; //抽象基类,实现了一个模板方法 class AbstractClass { protected: virtual void PrimitiveOperation1() = 0; //纯虚函数 virtual void PrimitiveOperation2() = 0; AbstractClass() { } public: //在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。 virtual ~AbstractClass() { } //模板方法,只在抽象基类中实现 void TemplateMethod() { this->PrimitiveOperation1(); this->PrimitiveOperation2(); } }; //具体子类,实现操作的特定细节 class ConcreteClass1:public AbstractClass { protected: void PrimitiveOperation1() { cout<<"ConcreteClass1...PrimitiveOperation1"<<endl; } void PrimitiveOperation2() { cout<<"ConcreteClass1...PrimitiveOperation2"<<endl; } public: ConcreteClass1(){ } ~ConcreteClass1(){ } }; //具体子类,实现操作的特定细节 class ConcreteClass2:public AbstractClass { protected: void PrimitiveOperation1() { cout<<"ConcreteClass2...PrimitiveOperation1"<<endl; } void PrimitiveOperation2() { cout<<"ConcreteClass2...PrimitiveOperation2"<<endl; } public: ConcreteClass2() { } ~ConcreteClass2(){ } }; int main() { AbstractClass* p1 = new ConcreteClass1(); AbstractClass* p2 = new ConcreteClass2(); p1->TemplateMethod(); p2->TemplateMethod(); return 0; } //---------输出--------- ConcreteClass1...PrimitiveOperation1 ConcreteClass1...PrimitiveOperation2 ConcreteClass2...PrimitiveOperation1 ConcreteClass2...PrimitiveOperation2
5、补充:
A、template是采用继承的方式实现功能的不同,其关键点就是将通用功能封装在抽象基类中,并将不同的功能细节放到子类中实现。采用一种称为:DIP(依赖倒置:Dependency Inversion Principles),就是父类去调用子类的功能,高层去控制底层,底层为高层的需要做基础,以实现不同的功能展示。
B、虚析构函数,在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。
C、注意:为防止恶意操作,一般模板方法都加上 final 关键词。
3.3、Chain of Responsibility(责任链)
1、意图:
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
2、角色:
A、Handler:请求接口;
B、ConcreteHandler:具体处理请求的接口;
3、适用性:
A、有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
B、你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
C、可处理一个请求的对象集合应被动态指定。
4、C++代码:
#include <iostream> using namespace std; //抽象处理类 class Handle { private: Handle* _succ; protected: Handle() { _succ = NULL; } public: virtual ~Handle() { delete _succ; } virtual void HandleRequest(int request) = 0; //设置其上级 void SetSuccessor(Handle* succ) { _succ = succ; } Handle* GetSuccessor() { return _succ; } }; //具体处理类 A class ConcreteHandleA: public Handle { public: void HandleRequest(int request) { if (request >= 0 && request < 500) cout << "ConcreteHandleA deal with: " << request <<endl; else if (this->GetSuccessor() != NULL) this->GetSuccessor()->HandleRequest(request); else cout << "Can't deal with " << request << endl; } }; //具体处理类 B class ConcreteHandleB: public Handle { public: void HandleRequest(int request) { if (request >= 500 && request < 1000) cout << "ConcreteHandleB deal with: " << request <<endl; else if (this->GetSuccessor() != NULL) this->GetSuccessor()->HandleRequest(request); else cout << "Can't deal with " << request << endl; } }; //具体处理类 C class ConcreteHandleC: public Handle { public: void HandleRequest(int request) { if (request >= 1000 && request < 2000) cout << "ConcreteHandleC deal with: " << request <<endl; else if (this->GetSuccessor() != NULL) this->GetSuccessor()->HandleRequest(request); else cout << "Can't deal with " << request << endl; } }; //测试 int main() { Handle* h1 = new ConcreteHandleA(); Handle* h2 = new ConcreteHandleB(); Handle* h3 = new ConcreteHandleC(); //设置其上级 h1A -> h2B -> h3C h1->SetSuccessor(h2); h2->SetSuccessor(h3); h1->HandleRequest(300); h1->HandleRequest(600); h1->HandleRequest(1500); h1->HandleRequest(3000); return 0; } //---------输出-------- ConcreteHandleA deal with: 300 ConcreteHandleB deal with: 600 ConcreteHandleC deal with: 1500 Can't deal with 3000
5、补充:
A、职责链的特点是:当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象负责处理它。
B、职责链的好处是:请求者不用管哪个对象来处理,反正该请求会被处理。它只需保持一个后继者即可。
C、 要注意的是:一个请求到链的最后可能也没有处理,所以一定要配置得当。
D、在 JAVA WEB 中遇到很多应用。
3.4、Command(命令)
1、意图:
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
2、角色:
A、Command:命令接口;
B、ConcreteCommand:具体的命令;
C、Invoker:命令执行者;
D、Receiver:命令的接受者;
3、适用性:
A、抽象出待执行的动作以参数化某对象,你可用过程语言中的回调(call back)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command 模式是回调机制的一个面向对象的替代品。
B、在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。
C、支持取消操作。Command的Excute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute和Execute来实现重数不限的“取消”和“重做”。
D、支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。
E、用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( transaction)的信息系统中很常见。一个事务封装了对数据的一组变动。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。
4、C++代码:
#include "iostream" #include <list> using namespace std; //实施与执行类 class Reciever { public: void Action() { cout<<"Do action !!"<<endl; } }; //抽象命令类 class Command { protected: Command() {} public: virtual ~Command() {} virtual void Excute() = 0; }; //Read 命令 class Read_Command:public Command { private: Reciever* _rev; public: Read_Command(Reciever* rev) { this->_rev = rev; } ~Read_Command() { delete this->_rev; } void Excute() { cout<<"Read Command..."<<endl; _rev->Action(); } }; //Write 命令 class Write_Command:public Command { private: Reciever* _rev; public: Write_Command(Reciever* rev) { this->_rev = rev; } ~Write_Command() { delete this->_rev; } void Excute() { cout<<"Write_Command..."<<endl; _rev->Action(); } }; //要求命令执行的类 class Invoker { private: Command* _cmd; list<Command*> cmdList; public: Invoker(Command* cmd) { _cmd = cmd; } Invoker() { } ~Invoker() { delete _cmd; } //通知执行类执行 void Notify() { list<Command*>::iterator it = cmdList.begin(); for (it; it != cmdList.end(); ++it) { _cmd = *it; _cmd->Excute(); } } //添加命令 void AddCmd(Command* pcmd) { cmdList.push_back(pcmd); } //删除命令 void DelCmd(Command* pcmd) { cmdList.remove(pcmd); } }; //测试代码 int main(int argc,char* argv[]) { Reciever* rev = new Reciever(); //定义一个执行类 Command* cmd1 = new Read_Command(rev);//Read 命令 Command* cmd2 = new Write_Command(rev);//Write 命令 Invoker inv; //管理所有命令 inv.AddCmd(cmd1); inv.AddCmd(cmd2); inv.Notify(); //通知执行类,执行 inv.DelCmd(cmd1); inv.Notify(); return 0; } //--------- Read Command... Do action !! Write_Command... Do action !! Write_Command... Do action !!
5、补充:
A、上例说明,在多线程程序中,多个用户都给系统发 Read 和 Write 命令。首先明确一点,所有的这些 Read 和 Write 命令都是调用一个库函数。其次用户并不需要知道别的用户的存在,也不管别人发不发命令,只管自己发命令,最后给结果即可。最后这些命令先是到了一个消息队列里面,然后由消息队列调用库函数。
B、优点:
a、它能比较容易地设计一个命令队列。
b、在需要的情况下,可以较容易地将命令记入日志。
c、允许接收请求的一方决定是否要否决请求。
d、可以容易地实现对请求的撤销和重做。
e、增加新的具体命令类很容易
f、把请求一个操作的对象(Command)与知道怎么执行一个操作的对象(Receiver)分割开来。
3.5、Iterator(迭代器)
1、意图:
提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
2、角色:
A、迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
B、具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
C、集合角色(Aggregate):集合角色负责提供创建具体迭代器角色的接口。
D、具体集合角色(Concrete Aggregate):具体集合角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该集合的结构相关。
3、适用性:
A、访问一个聚合对象的内容而无需暴露它的内部表示。
B、支持对聚合对象的多种遍历。
C、为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。
4、C++代码:
#include <iostream> #include <vector> using namespace std; template<class Item> class Iterator { public: virtual void first()=0; virtual void next()=0; virtual Item* currentItem()=0; virtual bool isDone()=0; virtual ~Iterator(){} }; template<class Item> class ConcreteAggregate; template<class Item> class ConcreteIterator : public Iterator <Item> { ConcreteAggregate<Item> * aggr; int cur; public: ConcreteIterator(ConcreteAggregate<Item>*a):aggr(a),cur(0){} virtual void first() { cur=0; } virtual void next() { if(cur<aggr->getLen()) cur++; } virtual Item* currentItem() { if(cur<aggr->getLen()) return &(*aggr)[cur]; else return NULL; } virtual bool isDone() { return (cur>=aggr->getLen()); } }; template<class Item> class Aggregate { public: virtual Iterator<Item>* createIterator()=0; virtual ~Aggregate(){} }; template<class Item> class ConcreteAggregate:public Aggregate<Item> { vector<Item >data; public: ConcreteAggregate() { data.push_back(1); data.push_back(2); data.push_back(3); } virtual Iterator<Item>* createIterator() { return new ConcreteIterator<Item>(this); } Item& operator[](int index) { return data[index]; } int getLen() { return data.size(); } }; int main() { Aggregate<int> * aggr =new ConcreteAggregate<int>(); Iterator<int> *it=aggr->createIterator(); for(it->first();!it->isDone();it->next()) cout<<*(it->currentItem())<<endl; delete it; delete aggr; return 0; } //---------输出--------- 1 2 3
5、补充:
A、在 STL 里提供 Iterator 来遍历 Vector 或者 List 数据结构。Iterator 模式也正是用来解决对一个聚合对象的遍历问题,将对聚合的遍历封装到一个类中进行,这样就避免暴露这个聚合对象的内部表示的可能。
B、在.NET下实现Iterator模式,对于聚集接口和迭代器接口已经存在了,其中IEnumerator扮演的就是迭代器的角色,而IEnumerable则扮演的就是抽象聚集的角色,她只有一个GetEnumerator()方法,如果集合对象需要具备跌代遍历的功能,就必须实现该接口。
3.6、Mediator(中介者)
1、意图:
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
2、角色:
A、Mediator:中介者,定义一个接口与各同事对象通信。
B、ConcreteMediator:具体中介者。
C、Colleague:同事接口
D、ConcreteColleage:具体同事类
3、适用性:
A、一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
B、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
C、想定制一个分布在多个类中的行为,而又不想生成太多的子类。
4、C++代码:
#include <iostream> #include <string> using namespace std; class Colleague; // 中介者类 class Mediator { public: virtual void Send(string message, Colleague* col) = 0; }; // 同事类 class Colleague { public: Colleague(Mediator* temp) { mediator = temp; } protected: Mediator* mediator; }; // 同事一 class ColleagueOne : public Colleague { public: ColleagueOne(Mediator* media) : Colleague(media) { } void Send(string strMessage) { mediator->Send(strMessage, this); } void Notify(string strMessage) { cout << "同事一获得了消息:" << strMessage << endl; } }; // 同事二 class ColleagueTwo : public Colleague { public: ColleagueTwo(Mediator* media) : Colleague(media) { } void Send(string strMessage) { mediator->Send(strMessage, this); } void Notify(string strMessage) { cout << "同事二获得了消息:" << strMessage << endl; } }; // 具体中介者类 class ConcreteMediator : public Mediator { public: ColleagueOne* colOne; ColleagueTwo* colTwo; void Send(string message, Colleague* col) { if (col == colOne) colTwo->Notify(message); else colOne->Notify(message); } }; int main() { ConcreteMediator* m = new ConcreteMediator(); // 让同事认识中介 ColleagueOne* colOne = new ColleagueOne(m); ColleagueTwo* colTwo = new ColleagueTwo(m); // 让中介认识具体的同事类 m->colOne = colOne; m->colTwo = colTwo; colOne->Send("吃饭了吗?"); colTwo->Send("还没吃,你请啊?"); return 0; } //---------输出--------- 同事二获得了消息:吃饭了吗? 同事一获得了消息:还没吃,你请啊?
5、补充:
A、优点是,各个 Colleague 减少了耦合。缺点是,由于 Mediator 控制了集中化,于是就把 Colleague 之间的交互复杂性变为了中介者的复杂性,也就是中介者会变的比任何一个 Colleague 都复杂。
B、Mediator 模式中,每个Colleague 维护一个 Mediator,当要进行通信时,每个具体的 Colleague 直接向ConcreteMediator 发信息,至于信息发到哪里,则由 ConcreteMediator 来决定。
C、ConcreteColleagueA 和 ConcreteColleagueB 不必维护对各自的引用,甚至它们也不知道各个的存在。
D、实现观察者模式的时候要注意:观察者和被观察对象之间的互动关系不能体现成类之间的直接调用,否则就将使观察者和被观察对象之间紧密的耦合起来
3.7、Memento(备忘录)
1、意图:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
2、角色:
A、Memento:备忘录,存储原发器的内部状态。
B、Originator:原发器,创建一个备忘录,泳衣记录当前时刻它的内部状态并能恢复备忘录的内部状态。
C、Caretaker:负责人,负责保存好备忘录。
3、适用性:
A、必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
B、如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
4、C++实现:
#include <iostream> #include <string> using namespace std; //备忘录类:负责存储 Originator 对象的内部状态 class Memento { //注意:备忘录类中的方法是私有的!!!!!!! private: string _sdt; //这是最关键的地方,将 Originator 为friend类。这样 Originator 就可以访问 Memento 类的内部信息 friend class Originator; Memento() {} Memento(const string& sdt) { _sdt = sdt; } void SetState(const string& sdt) { _sdt = sdt; } string GetState() { return _sdt; } }; //原始类。负责创建备忘录 class Originator { private: string _sdt; Memento* _mt; public: Originator() { _sdt = ""; _mt = 0; } Originator(const string& sdt) { _sdt = sdt; _mt = 0; } //创建备忘录,将当前信息保存在一个备忘录对象中 Memento* CreateMemento() { return new Memento(_sdt); } //恢复备忘录,将备忘录对象上的信息恢复出来 void RestoreToMemento(Memento* mt) { this->_sdt = mt->GetState(); } string GetState() { return _sdt; } void SetState(const string& sdt) { _sdt = sdt; } void PrintState() { cout<<this->_sdt<<"....."<<endl; } }; //测试代码 int main(int argc,char* argv[]) { Originator* o = new Originator(); o->SetState("old"); //备忘以前的状态 o->PrintState(); Memento* m = o->CreateMemento(); //创建一个备忘录 o->SetState("new"); //修改状态 o->PrintState(); o->RestoreToMemento(m); //恢复修改前的状态 o->PrintState(); return 0; } //---------输出--------- old..... new..... old.....
5、补充:
A、memento是一个保存另外一个对象内部状态拷贝的对象.这样以后就可以将该对象恢复到原先保存的状态.
B、实现Memento模式时,需要注意以下两点:
a、Memento对象需要支持两类接口:为Originator提供的宽接口和为其他对象提供的窄接口,也就是说,应该对Originator以外的对象隐藏Originator相关的接口。C++中可以用友元函数实现,而备忘录类中的方法是私有的。
b、Memento对象可以保存Originator对象中的所有状态,但是,如果状态变化是顺序的,那么每个Memento对象可以只保存状态改变的增量。也就是保存的是操作前对象的状态,而不是操作后对象的状态;否则就没办法做恢复了。
C、可以运用 Java 中的对象序列化、反序列化机制来实现备份的功能,可以将一个对象的内部状态序列化称为二进制流,保存到文件或数据库中,需要时再反序列化回来。
3.8、Observer(观察者)
1、意图:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
2、角色:
A、Subject:被观察者接口
B、ConcreteSubject:具体被观察者
C、Observer:观察者
D、ConcreteObserver:具体的观察者
3、适用性:
A、当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
B、当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
C、当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
4、C++代码:
#include <iostream> #include <string> #include <list> using namespace std; class Subject; //观察者基类 class Observer { protected: string _st; Observer() { _st = '\0'; } public: virtual ~Observer(){ } virtual void Update(Subject* sub) = 0; virtual void PrintInfo() = 0; }; //被观察者接口 class Subject { private: list<Observer* >* _obvs; protected: Subject() { _obvs = new list<Observer*>; } public: virtual ~Subject(){ } //注册观察者,这样通知者就能通知到观察者 virtual void Attach(Observer* obv) { _obvs->push_front(obv); } //注销观察者,通知者不再通知观察者 virtual void Detach(Observer* obv) { if (obv != NULL) _obvs->remove(obv); } //通知操作,通知后对于每个注册过的观察者,将会调用自己的update方法 virtual void Notify() { list<Observer*>::iterator it; it = _obvs->begin(); for (;it != _obvs->end();it++) (*it)->Update(this); } virtual void SetState(const string& st) = 0; virtual string GetState() = 0; }; //具体被观察者:数据 class DataSubject:public Subject { private: string _st; public: DataSubject() { _st = '\0'; } ~DataSubject(){ } string GetState() { return _st; } void SetState(const string& st) { _st = st; } }; //具体观察者:数据表格 class SheetObserver:public Observer { private: Subject* _sub; public: virtual Subject* GetSubject() { return _sub; } //构造函数里,把自己注册到通知者里 SheetObserver(Subject* sub) { _sub = sub; _sub->Attach(this); } virtual ~SheetObserver() { _sub->Detach(this); if (_sub != 0) delete _sub; } //更新操作 void Update(Subject* sub) { _st = sub->GetState(); //具体的数据可以从Subject这个通知者中取 PrintInfo(); } void PrintInfo() { cout<<"Sheet observer.... "<<_sub->GetState()<<endl; } }; //具体观察者:数据图表 class ChatObserver:public Observer { private: Subject* _sub; public: virtual Subject* GetSubject() { return _sub; } ChatObserver(Subject* sub) { _sub = sub; _sub->Attach(this); } virtual ~ChatObserver() { _sub->Detach(this); if (_sub != 0) delete _sub; } //更新操作 void Update(Subject* sub) { _st = sub->GetState(); PrintInfo(); } void PrintInfo() { cout<<"Chat observer.... "<<_sub->GetState()<<endl; } }; //测试 int main() { DataSubject* sub = new DataSubject();//具体被观察者:数据通知者 Observer* o1 = new SheetObserver(sub);//表格观察者 Observer* o2 = new ChatObserver(sub);//图表观察者 sub->SetState("old data");//数据发生变化 sub->Notify();//通知者下发通知 sub->SetState("new data"); sub->Notify(); o1->Update(sub); //也可以由观察者自己调用更新函数 return 0; } //---------输出-------- Chat observer.... old data Sheet observer.... old data Chat observer.... new data Sheet observer.... new data Sheet observer.... new data
5、补充:
A、实现观察者模式的时候要注意:
a、观察者和被观察对象之间的互动关系不能体现成类之间的直接调用,否则就将使观察者和被观察对象之间紧密的耦合起来,从根本上违反面向对象的设计的原则。
b、避免循环引用
c、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
B、JAVA 中已经有了对观察者模式的支持类。
C、MVC模式是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。观察者模式可以用来实现MVC模式,观察者模式中的观察目标就是MVC模式中的模型(Model),而观察者就是MVC中的视图(View),控制器(Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容。
3.9、State(状态)
1、意图:
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
2、角色:
A、Context: 环境类
B、State: 抽象状态类
C、ConcreteState: 具体状态类
3、适用性:
A、一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
B、一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
4、C++实现:
#include "iostream" using namespace std; class Context; class ConcreteStateA; class State { public: State() { } virtual void Handle(Context* pContext)=0; }; class Context { private: State* m_State; public: Context(State* pState) { m_State=pState; } void Request() { if (NULL!=m_State) m_State->Handle(this); } void ChangState(State* pState) { if (NULL!=m_State) { delete m_State; m_State=NULL; } m_State=pState; } }; class ConcreteStateB: public State { public: ConcreteStateB(); void Handle(Context* pContext); }; class ConcreteStateA:public State { public: void Handle(Context* pContext) { cout<<"Handle by ConcreteStateA"<<endl; if (NULL!=pContext) pContext->ChangState(new ConcreteStateB()); } }; ConcreteStateB::ConcreteStateB() { } void ConcreteStateB::Handle(Context* pContext) { cout<<"Handle by ConcreteStateB"<<endl; if (NULL!=pContext) pContext->ChangState(new ConcreteStateA()); } int main() { State* pState=new ConcreteStateA(); Context* pContext=new Context(pState); pContext->Request(); pContext->Request(); pContext->Request(); delete pContext; return 0; } //---------输出--------- Handle by ConcreteStateA Handle by ConcreteStateB Handle by ConcreteStateA
5、补充:
A、注意:
状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。
B、在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
3.10、Strategy(策略)
1、意图:
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
2、角色:
A、Strategy:策略
B、ConcreteStrategy:具体策略
C、Context:使用环境
3、适用性:
A、许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
B、需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时[H087] ,可以使用策略模式。
C、算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
D、一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
4、C++代码:
#include "iostream" using namespace std; class Strategy { public: virtual void AlgorithmInterface()=0; }; class ConcreteStrategyA:public Strategy { public: void AlgorithmInterface() { cout<<"AlgorithmInterface A"<<endl; } }; class ConcreteStrategyB:public Strategy { public: void AlgorithmInterface() { cout<<"AlgorithmInterface B"<<endl; } }; class ConcreteStrategyC:public Strategy { public: void AlgorithmInterface() { cout<<"AlgorithmInterface C"<<endl; } }; class Context { private: Strategy* p_Strategy; public: Context(Strategy* p_s) { p_Strategy=p_s; } void ContextInterface() { if (NULL!=p_Strategy) p_Strategy->AlgorithmInterface(); } }; int main() { Strategy* pStrategy=new ConcreteStrategyA(); Context* pContext=new Context(pStrategy); pContext->ContextInterface(); return 0; } //---------输出-------- AlgorithmInterface A
5、补充:
A、注意:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
B、Bridge模式和Strategy模式相似就是因为他们都将任务委托给了另外一个接口的具体实现,他们之间的区别在于Bridge的目的是让底层实现和上层接口可以分别演化,从而提高移植性而Strategy的目的是将复杂的算法封装起来,从而便于替换不同的算法。
3.11、Visitor(访问者)
1、意图:
表示一个作用于某对象结构中的各元素的操作。它你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
2、角色:
A、Visitor:访问者
B、ConcreteVistor:具体的访问者
C、Element:元素
D、ConcreteElement:具体元素
E、ObjectStructure:对象结构
3、适用性:
A、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
B、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
4、C++实现:
#include <iostream> #include <vector> using namespace std; class ConcreteElementA; class ConcreteElementB; class Visitor { public: virtual void VisitConcreteElementA(ConcreteElementA *pElementA) = 0; virtual void VisitConcreteElementB(ConcreteElementB *pElementB) = 0; }; class ConcreteVisitor1 : public Visitor { public: void VisitConcreteElementA(ConcreteElementA *pElementA) { cout<<"ConcreteVisitor1 VisitConcreteElementA"<<endl; } void VisitConcreteElementB(ConcreteElementB *pElementB) { cout<<"ConcreteVisitor1 VisitConcreteElementB"<<endl; } }; class ConcreteVisitor2 : public Visitor { public: void VisitConcreteElementA(ConcreteElementA *pElementA) { cout<<"ConcreteVisitor2 VisitConcreteElementA"<<endl; } void VisitConcreteElementB(ConcreteElementB *pElementB) { cout<<"ConcreteVisitor2 VisitConcreteElementB"<<endl; } }; // Element object class Element { public: virtual void Accept(Visitor *pVisitor) = 0; }; class ConcreteElementA : public Element { public: void Accept(Visitor *pVisitor) { pVisitor->VisitConcreteElementA(this); } }; class ConcreteElementB : public Element { public: void Accept(Visitor *pVisitor) { pVisitor->VisitConcreteElementB(this); } }; // ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素 class ObjectStructure { private: vector<Element *> elements; public: void Attach(Element *pElement) { elements.push_back(pElement); } void Detach(Element *pElement) { vector<Element *>::iterator it = find(elements.begin(), elements.end(), pElement); if (it != elements.end()) elements.erase(it); } void Accept(Visitor *pVisitor) { // 为每一个element设置visitor,进行对应的操作 for (vector<Element *>::const_iterator it = elements.begin(); it != elements.end(); ++it) (*it)->Accept(pVisitor); } }; int main() { ObjectStructure *pObject = new ObjectStructure; Element *pElementA = new ConcreteElementA; Element *pElementB = new ConcreteElementB; pObject->Attach(pElementA); pObject->Attach(pElementB); Visitor *pVisitor1 = new ConcreteVisitor1; Visitor *pVisitor2 = new ConcreteVisitor2; pObject->Accept(pVisitor1); pObject->Accept(pVisitor2); if (pVisitor2) delete pVisitor2; if (pVisitor1) delete pVisitor1; if (pElementB) delete pElementB; if (pElementA) delete pElementA; if (pObject) delete pObject; return 0; } //---------输出-------- ConcreteVisitor1 VisitConcreteElementA ConcreteVisitor1 VisitConcreteElementB ConcreteVisitor2 VisitConcreteElementA ConcreteVisitor2 VisitConcreteElementB
5、补充:
A、访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
B、使用访问者模式前提是对象群结构中(Collection) 中的对象类型很少改变。
四、参考
1、总结:http://www.cnblogs.com/beijiguangyong/archive/2010/11/15/2302807.html
2、代码:http://blog.csdn.net/conanswp/article/category/1134076
http://blog.csdn.net/lwbeyond/article/category/1113025
3、原则:http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html
4、很全面的文档:
http://design-patterns.readthedocs.org/zh_CN/latest/creational_patterns/creational.html
http://www.runoob.com/design-pattern/observer-pattern.html 【推荐】
转载标明出处:https://blog.evanxia.com/2016/03/242