代理模式:Proxy
- 动机
“增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方案。在面向对象系统中,直接食用某些对象会带来很多问题,作为间接层的proxy对象便是解决这一问题的常见手段。
2.伪代码:
class ISubject{
public:virtual void process();
};//Proxy的设计
class SubjectProxy: public ISubject{public:virtual void process(){//对RealSubject的一种间接访问//....}
};class ClientApp{ISubject* subject;public:ClientApp(){subject=new SubjectProxy();}void DoTask(){//...subject->process();//....}
};
3.理解:
copy on write就是这个思想。看类图以及代码简单,但是它真正的实现可能会很复杂。
4.类图:
适配器:Adapter
- 动机
需要将一些现存的对象放在新的环境中应用,但是新环境要求的接口是这些现存对象所不能满足的。如何应对这种迁移的变化?如何既能利用现存对象的良好实现,同时又能满足新的应用环境的要求接口?
2.伪代码:
//目标接口(新接口)
class ITarget{
public:virtual void process()=0;
};//遗留接口(老接口)
class IAdaptee{
public:virtual void foo(int data)=0;virtual int bar()=0;
};//遗留类型
class OldClass: public IAdaptee{//....
};//对象适配器
class Adapter: public ITarget{ //继承
protected:IAdaptee* pAdaptee;//组合public:Adapter(IAdaptee* pAdaptee){this->pAdaptee=pAdaptee;}virtual void process(){int data=pAdaptee->bar();pAdaptee->foo(data);}
};//类适配器
class Adapter: public ITarget,protected OldClass{ //多继承
}int main(){IAdaptee* pAdaptee=new OldClass();ITarget* pTarget=new Adapter(pAdaptee);pTarget->process();}class stack{deqeue container;};class queue{deqeue container;};
3.类图:
中介者模式:Mediator
- 动机
会出现多个对象互相交互的情况,对象之间通常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将会面临不断变化。
2.伪代码:
与facade相同,思想,无代码
3.理解:
Colleague和Mediator两个类之间相互依赖,Colleague之间不相互依赖,但是会通过Mediator间接依赖。把直接依赖关系变为间接依赖关系。facade用于解耦内部与外部之间的关系,中介者相当于解耦facade内部之间的关系。
4.类图:
状态变化
在组件变化构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?
状态模式:State
- 动机
某些对象的状态如果改变,其行为也会随之改变。如何在运行时根据对象的状态来透明的更改对象的行为?而不会为对象操作和状态转变之间引入紧耦合?
2.伪代码:
class NetworkState{public:NetworkState* pNext;virtual void Operation1()=0;virtual void Operation2()=0;virtual void Operation3()=0;virtual ~NetworkState(){}
};class OpenState :public NetworkState{static NetworkState* m_instance;
public:static NetworkState* getInstance(){if (m_instance == nullptr) {m_instance = new OpenState();}return m_instance;}void Operation1(){//**********pNext = CloseState::getInstance();}void Operation2(){//..........pNext = ConnectState::getInstance();}void Operation3(){//$$$$$$$$$$pNext = OpenState::getInstance();}};class CloseState:public NetworkState{ }
//...class NetworkProcessor{NetworkState* pState;public:NetworkProcessor(NetworkState* pState){this->pState = pState;}void Operation1(){//...pState->Operation1();pState = pState->pNext;//...}void Operation2(){//...pState->Operation2();pState = pState->pNext;//...}void Operation3(){//...pState->Operation3();pState = pState->pNext;//...}};
备忘录模式:Memento
- 动机
某些对象的状态在转化过程中,可能有某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如何在不破坏封装性的前提下实现这个任务呢?
2.伪代码:
class Memento
{string state;//..
public:Memento(const string & s) : state(s) {}string getState() const { return state; }void setState(const string & s) { state = s; }
};class Originator
{string state;//....
public:Originator() {}Memento createMomento() {Memento m(state);return m;}void setMomento(const Memento & m) {state = m.getState();}
};int main()
{Originator orginator;//捕获对象状态,存储到备忘录Memento mem = orginator.createMomento();//... 改变orginator状态//从备忘录中恢复orginator.setMomento(mem);
}
3.理解:
备忘录就是存储一个类的状态快照,在需要的时候设置为备忘录中的状态。核心是隐藏信息。这个模式有些过时。其实就是对象序列化。
4.类图:
数据结构
一些组件在内部具有特定的数据结构,如果让客户程序依赖于这些特定的数据结构,将破坏组件的复用性。这个时候,将这些数据结构封装在内部,对外提供统一的接口,来实现与特定的数据结构无关的访问。
组合模式:Composite
- 动机
如何将客户代码与复杂的对象容器结构解耦?让对象容器自己实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?
2.伪代码:
#include <iostream>
#include <list>
#include <string>
#include <algorithm>using namespace std;class Component
{
public:virtual void process() = 0;virtual ~Component(){}
};//树节点
class Composite : public Component{string name;list<Component*> elements;
public:Composite(const string & s) : name(s) {}void add(Component* element) {elements.push_back(element);}void remove(Component* element){elements.remove(element);}void process(){//1. process current node//2. process leaf nodesfor (auto &e : elements)e->process(); //多态调用}
};//叶子节点
class Leaf : public Component{string name;
public:Leaf(string s) : name(s) {}void process(){//process current node}
};void Invoke(Component & c){//...c.process();//...
}int main()
{Composite root("root");Composite treeNode1("treeNode1");Composite treeNode2("treeNode2");Composite treeNode3("treeNode3");Composite treeNode4("treeNode4");Leaf leaf1("left1");Leaf leaf2("left2");root.add(&treeNode1);treeNode1.add(&treeNode2);treeNode2.add(&leaf1);root.add(&treeNode3);treeNode3.add(&treeNode4);treeNode4.add(&leaf2);process(root);process(leaf2);process(treeNode3);}
3.理解:
对象组合成树形结构。和装饰器有点像,不仅has-a,而且is-a。将一对多的关系替换成一对一的关系。
4.类图:
迭代器:Iterator
- 动机
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
2.伪代码:
template<typename T>
class Iterator
{
public:virtual void first() = 0;virtual void next() = 0;virtual bool isDone() const = 0;virtual T& current() = 0;
};template<typename T>
class MyCollection{public:Iterator<T> GetIterator(){//...}};template<typename T>
class CollectionIterator : public Iterator<T>{MyCollection<T> mc;
public:CollectionIterator(const MyCollection<T> & c): mc(c){ }void first() override {}void next() override {}bool isDone() const override{}T& current() override{}
};void MyAlgorithm()
{MyCollection<int> mc;Iterator<int> iter= mc.GetIterator();for (iter.first(); !iter.isDone(); iter.next()){cout << iter.current() << endl;}}
3.理解:
对C++来说,面向对象的迭代器已经过时了。STL中的迭代器是泛型编程思想。运行时绑定是没有编译时绑定效率高的。
4.类图:
职责链模式:Chain Of Responsibility
- 动机
一个请求可能有多个接收者,但是真正的接受者,也就是去处理这个请求的只有一个。这时,请求发送者与接受者的耦合可能出现变化脆弱的情况。
2.伪代码:
#include <iostream>
#include <string>using namespace std;enum class RequestType
{REQ_HANDLER1,REQ_HANDLER2,REQ_HANDLER3
};class Reqest
{string description;RequestType reqType;
public:Reqest(const string & desc, RequestType type) : description(desc), reqType(type) {}RequestType getReqType() const { return reqType; }const string& getDescription() const { return description; }
};class ChainHandler{ChainHandler *nextChain;void sendReqestToNextHandler(const Reqest & req){if (nextChain != nullptr)nextChain->handle(req);}
protected:virtual bool canHandleRequest(const Reqest & req) = 0;virtual void processRequest(const Reqest & req) = 0;
public:ChainHandler() { nextChain = nullptr; }void setNextChain(ChainHandler *next) { nextChain = next; }void handle(const Reqest & req){if (canHandleRequest(req))processRequest(req);elsesendReqestToNextHandler(req);}
};class Handler1 : public ChainHandler{
protected:bool canHandleRequest(const Reqest & req) override{return req.getReqType() == RequestType::REQ_HANDLER1;}void processRequest(const Reqest & req) override{cout << "Handler1 is handle reqest: " << req.getDescription() << endl;}
};class Handler2 : public ChainHandler{
protected:bool canHandleRequest(const Reqest & req) override{return req.getReqType() == RequestType::REQ_HANDLER2;}void processRequest(const Reqest & req) override{cout << "Handler2 is handle reqest: " << req.getDescription() << endl;}
};class Handler3 : public ChainHandler{
protected:bool canHandleRequest(const Reqest & req) override{return req.getReqType() == RequestType::REQ_HANDLER3;}void processRequest(const Reqest & req) override{cout << "Handler3 is handle reqest: " << req.getDescription() << endl;}
};int main(){Handler1 h1;Handler2 h2;Handler3 h3;h1.setNextChain(&h2);h2.setNextChain(&h3);Reqest req("process task ... ", RequestType::REQ_HANDLER3);h1.handle(req);return 0;
}
3.理解:
应用的不多。使得多个接收者都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理请求为止。
4.类图:
行为变化
在组件的构建过程中,组件行为的变化经常导致组件本身的剧烈变化。下面的模式将组件本身和组件的行为进行解耦
命令模式:Command
- 动机
如何将行为请求者和行为实现者解耦?
2.伪代码:
#include <iostream>
#include <vector>
#include <string>
using namespace std;class Command
{
public:virtual void execute() = 0;
};class ConcreteCommand1 : public Command
{string arg;
public:ConcreteCommand1(const string & a) : arg(a) {}void execute() override{cout<< "#1 process..."<<arg<<endl;}
};class ConcreteCommand2 : public Command
{string arg;
public:ConcreteCommand2(const string & a) : arg(a) {}void execute() override{cout<< "#2 process..."<<arg<<endl;}
};class MacroCommand : public Command
{vector<Command*> commands;
public:void addCommand(Command *c) { commands.push_back(c); }void execute() override{for (auto &c : commands){c->execute();}}
};int main()
{ConcreteCommand1 command1(receiver, "Arg ###");ConcreteCommand2 command2(receiver, "Arg $$$");MacroCommand macro;macro.addCommand(&command1);macro.addCommand(&command2);macro.execute();}
3.理解:
有点和仿函数思想类似。将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化。
4.类图:
访问器模式:Visitor
- 动机
在软件构件的过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为,如果直接在基类中作出修改,将会给子类带来繁重的变更负担,甚至破坏原有设计。
2.伪代码:
#include <iostream>
using namespace std;class Visitor;class Element
{
public:virtual void accept(Visitor& visitor) = 0; //第一次多态辨析virtual ~Element(){}
};class ElementA : public Element
{
public:void accept(Visitor &visitor) override {visitor.visitElementA(*this);}};class ElementB : public Element
{
public:void accept(Visitor &visitor) override {visitor.visitElementB(*this); //第二次多态辨析}};class Visitor{
public:virtual void visitElementA(ElementA& element) = 0;virtual void visitElementB(ElementB& element) = 0;virtual ~Visitor(){}
};//==================================//扩展1
class Visitor1 : public Visitor{
public:void visitElementA(ElementA& element) override{cout << "Visitor1 is processing ElementA" << endl;}void visitElementB(ElementB& element) override{cout << "Visitor1 is processing ElementB" << endl;}
};//扩展2
class Visitor2 : public Visitor{
public:void visitElementA(ElementA& element) override{cout << "Visitor2 is processing ElementA" << endl;}void visitElementB(ElementB& element) override{cout << "Visitor2 is processing ElementB" << endl;}
};int main()
{Visitor2 visitor;ElementB elementB;elementB.accept(visitor);// double dispatchElementA elementA;elementA.accept(visitor);return 0;
}
3.理解:
Visitor中要求具体的Element稳定,这个条件通常很难保证,这是这个模式的重大缺点。accept方法表示接受一个visitor,这个visitor能够为该类增添新的方法。一般不用,因为前提条件很苛刻。
Visitor通过双重分发来实现不更改Element层次结构的前提下,在运行时透明的为类层次结构上的各个类添加新的操作。
适用于:Element类层次结构稳定,而其中的操作确实频繁改动的。(设计思想:有动的,有不动的)
4.类图:
领域规则
某些领域,变化虽然频繁,但是可以抽象为某种规则。要给出该领域下的对于变化的一般性解决方案。
解析器模式:Interpreter
- 动机
如果特定领域的某一问题比较复杂,类似的结构不断出现,如果使用普通的编程方式来实现将会面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。
2.伪代码:
#include <iostream>
#include <map>
#include <stack>using namespace std;class Expression {
public:virtual int interpreter(map<char, int> var)=0;virtual ~Expression(){}
};//变量表达式
class VarExpression: public Expression {char key;public:VarExpression(const char& key){this->key = key;}int interpreter(map<char, int> var) override {return var[key];}};//符号表达式
class SymbolExpression : public Expression {// 运算符左右两个参数
protected:Expression* left;Expression* right;public:SymbolExpression( Expression* left, Expression* right):left(left),right(right){}};//加法运算
class AddExpression : public SymbolExpression {public:AddExpression(Expression* left, Expression* right):SymbolExpression(left,right){}int interpreter(map<char, int> var) override {return left->interpreter(var) + right->interpreter(var);}};//减法运算
class SubExpression : public SymbolExpression {public:SubExpression(Expression* left, Expression* right):SymbolExpression(left,right){}int interpreter(map<char, int> var) override {return left->interpreter(var) - right->interpreter(var);}};Expression* analyse(string expStr) {stack<Expression*> expStack;Expression* left = nullptr;Expression* right = nullptr;for(int i=0; i<expStr.size(); i++){switch(expStr[i]){case '+':// 加法运算left = expStack.top();right = new VarExpression(expStr[++i]);expStack.push(new AddExpression(left, right));break;case '-':// 减法运算left = expStack.top();right = new VarExpression(expStr[++i]);expStack.push(new SubExpression(left, right));break;default:// 变量表达式expStack.push(new VarExpression(expStr[i]));}}Expression* expression = expStack.top();return expression;
}void release(Expression* expression){//释放表达式树的节点内存...
}int main(int argc, const char * argv[]) {string expStr = "a+b-c+d-e";map<char, int> var;var.insert(make_pair('a',5));var.insert(make_pair('b',2));var.insert(make_pair('c',1));var.insert(make_pair('d',6));var.insert(make_pair('e',10));Expression* expression= analyse(expStr);int result=expression->interpreter(var);cout<<result<<endl;release(expression);return 0;
}
3.理解:
可以理解为四则运算的运算树。使用场景为:业务规则频繁变化,且类似的结构重复出现,并且容易(简单简单,小而简单的问题)抽象为有规则的问题。
4.类图:
总结
- 模版方法:用于制定一套模板,别人来用的时候按照模板实现
- 策略模式:方便新增策略,只需新增一个子类
- 观察者模式:方便通知消息
- 装饰模式:方便拓展功能
- 桥模式:用于桥接两个向不同纬度变化的类,这个桥,就是一个类中组合另外一个类
- 工厂方法:创建对象解耦合
- 抽象工厂:工厂的工厂,要生产一系列的产品,这一系列产品需要配套
- 原型方法:有clone方法来完成对象创建
- 构建者:分步骤构建一个对象,将表示和构建分离
- 单件模式:保证在内存中一个类只存在一个实例
- 享元模式:运用共享技术有效地支持大量细粒度的对象,大量大量!线程池
- 门面模式:不管facade内部怎么变化,对于用户来说,接口都是一样的
- 代理模式:就像VPN一样,间接控制。
- 适配器:改变原始类接口,适配新类。比如STL中的stack
- 中介者模式:把直接依赖关系变为间接依赖关系
- 状态模式:将策略模式的策略改为状态
- 备忘录模式:存储一个类的状态快照,在需要的时候设置为备忘录中的状态
- 组合模式:对象组合成树形结构。将一对多的关系替换成一对一的关系
- 迭代器:STL中的迭代器思想
- 职责链:处理请求的对象连成一条链
- 命令模式:将行为抽象为对象,有点和仿函数类似
- 访问器模式:使用双重分发的机制,透明的添加新的操作
- 解释器模式:四则运算树
参考文章:侯捷C++八部曲笔记(三、设计模式)_侯捷c++设计模式-CSDN博客