目录
第三部分:行为模式
1. Template模式
a)结构图
算法实现在不同的对象中有不同的细节实现,但是逻辑(算法)的框架(或通用的应用算法)是相同的。Template提供了这种情况的一个实现框架。该问题可以采取两种模式来解决,一是Template模式,二是Strategy模式。
Template模式是采用继承的方式实现这一点:将逻辑(算法)框架放在抽象基类中,并定义好细节的接口,子类中实现细节。
Template模式实际上就是利用面向对象中多态的概念实现算法实现细节和高层接口的松耦合
b)实例
main 实际上就是多态
#include "concrete_class.h"
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){delete(p); (p)=NULL;} }
#endif
int main()
{
// 阿里校招
Company *alibaba = new Alibaba();
alibaba->Recruit();
// 腾讯校招
Company *tencent = new Tencent();
tencent->Recruit();
SAFE_DELETE(tencent);
SAFE_DELETE(alibaba);
getchar();
return 0;
}
抽象类
#ifndef ABSTRACT_CLASS_H
#define ABSTRACT_CLASS_H
#include <iostream>
// 公司
class Company
{
public:
virtual ~Company() {}
// 校园招聘
void Recruit() {
std::cout << "---------- Begin ----------" << std::endl;
CareerTalk();
ReceiveResume();
Interview();
Offer();
std::cout << "---------- End ----------" << std::endl;
}
// 宣讲会
void CareerTalk() {
std::cout << "Delivery" << std::endl;
}
// 接收简历
void ReceiveResume() {
std::cout << "Receive Resume" << std::endl;
}
protected:
// 面试
virtual void Interview() = 0;
// 发放 Offer
virtual void Offer() = 0;
};
#endif // ABSTRACT_CLASS_H
派生实现
#ifndef CONCRETE_CLASS_H
#define CONCRETE_CLASS_H
#include "abstract_class.h"
#include <iostream>
// 阿里
class Alibaba : public Company
{
protected:
virtual void Interview() override {
std::cout << "First interview -> Second interview -> Third interview" << std::endl;
}
virtual void Offer() override {
std::cout << "30W" << std::endl;
}
};
// 腾讯
class Tencent : public Company
{
protected:
virtual void Interview() override {
std::cout << "First interview -> Second interview" << std::endl;
}
virtual void Offer() override {
std::cout << "25W" << std::endl;
}
};
#endif // CONCRETE_CLASS_H
我们将原语操作(细节算法)定义未保护(Protected)成员,只供模板方法调用(子类可以)。
c)讨论
- 关键点就是将通用算法封装在抽象基类, 并将不同的算法细节放到子类中实现
- Template模式获得一种反向控制结构效果,这也是面向对象系统的分析和设计中一个原则DIP(依赖倒置:Dependency Inversion Principles)。其含义就是父类调用子类的操作(高层模块调用低层模块的操作),低层模块实现高层模块声明的接口。这样控制权在父类(高层模块),低层模块反而要依赖高层模块。
- 不足在于:ConcreteClass类中的实现的方法Primitive1(),是不能被别的类复用的(Tencent的Interview()不能被别的抽象类使用),如果我们新建另一个抽象类社会招聘,不能复用class Alibaba的Interview实现,因为Alibaba已经继承了Company校园招聘类,也就是说继承了其通用算法Recruit,CareerTalk等函数,社会招聘也有同样的通用函数,因此不能复用(没有继承社会招聘类)。
- 上述缺点也是继承固有的问题,Strategy模式则通过组合(委托)来达到和Template模式类似的效果,可以解决这个问题,其代价就是空间和时间上的代价。
2. Strategy模式
a)结构图
Strategy模式和Template模式要解决的问题是相同(类似)的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的解耦。
trategy模式将逻辑(算法)封装到一个类(Context)里面,通过组合的方式将具体算法的实现在组合对象中实现,再通过委托的方式将抽象接口的实现委托给组合对象实现。
关键就是将算法的逻辑抽象接口(DoAction)封装到一个类中(Context),再通过委托的方式将具体的算法实现委托给具体的Strategy类来实现(ConcreteStrategeA类)。
b)实例
#include "context.h"
#include "concrete_strategy.h"
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){delete(p); (p)=NULL;} }
#endif
int main()
{
// 策略之间可以相互替换
IStrategy *bike = new BikeStrategy();
IStrategy *car = new CarStrategy();
IStrategy *train = new TrainStrategy();
Context *bikeContext = new Context(bike);
Context *carContext = new Context(car);
Context *trainContext = new Context(train);
bikeContext->Travel();
carContext->Travel();
trainContext->Travel();
SAFE_DELETE(bike);
SAFE_DELETE(car);
SAFE_DELETE(train);
SAFE_DELETE(bikeContext);
SAFE_DELETE(carContext);
SAFE_DELETE(trainContext);
return 0;
}
这个是重点
#ifndef CONTEXT_H
#define CONTEXT_H
#include "strategy.h"
class Context
{
public:
Context(IStrategy *strategy) { m_pStrategy = strategy; }
void Travel() { m_pStrategy->Travel(); }
private:
IStrategy *m_pStrategy; //定义了指针,即通过组合实现,而Template模式则采取的是继承的方式,类空间大一些,时间效率低一些
};
#endif // CONTEXT_H
#ifndef STRATEGY_H
#define STRATEGY_H
// 出行策略
class IStrategy
{
public:
virtual void Travel() = 0;
};
#endif // STRATEGY_H
#ifndef CONCRETE_STRATEGY_H
#define CONCRETE_STRATEGY_H
#include "strategy.h"
#include <iostream>
// 骑自行车
class BikeStrategy : public IStrategy
{
public:
virtual void Travel() override { std::cout << "Travel by bike" << std::endl; }
};
// 开车
class CarStrategy : public IStrategy
{
public:
virtual void Travel() override { std::cout << "Travel by car" << std::endl; }
};
// 坐火车
class TrainStrategy : public IStrategy
{
public:
virtual void Travel() override { std::cout << "Travel by train" << std::endl; }
};
#endif // CONCRETE_STRATEGY_H
c)讨论
Strategy模式和Template模式实际是实现一个抽象接口的两种方式:继承和组合之间的区别。要实现一个抽象接口,继承是一种方式:我们将抽象接口声明在基类中,将具体的实现放在具体子类中。组合(委托)是另外一种方式:我们将接口的实现放在被组合对象中,将抽象接口放在组合类中。这两种方式各有优缺点:
- 继承:
优点:易于修改和扩展那些被复用的实现
缺点
1)破坏了封装性,继承中父类的实现细节暴露给子类了;
2)当父类的实现更改时,其所有子类将不得不随之改变
3)从父类继承而来的实现在运行期间不能改变(编译期间就已经确定了)。
4)“白盒”复用,原因在1)中;
- 组合
优点:
1)“黑盒”复用,因为被包含对象的内部细节对外是不可见的;
2)封装性好,原因为1);
3)实现和抽象的依赖性很小(组合对象和被组合对象之间的依赖性小);
4)可以在运行期间动态定义实现(通过一个指向相同类型的指针,典型的是抽象基类的指针)。
缺点:
1)系统中对象过多。
- 组合相比继承可以取得更好的效果,因此在面向对象的设计中的有一条很重要的原则就是:优先使用(对象)组合,而非(类)继承.
- 继承是一种强制性很强的方式,因此也使得基类和具体子类之间的耦合性很强。例如在Template模式中在ConcreteClass1中定义的原语操作别的类是不能够直接复用(除非你继承自AbstractClass,具体分析请参看Template模式文档)。而组合(委托)的方式则有很小的耦合性,实现(具体实现)和接口(抽象接口)之间的依赖性很小,例如在本实现中,ConcreteStrategyA的具体实现操作很容易被别的类复用,例如我们要定义另一个Context类AnotherContext,只要组合一个指向Strategy的指针就可以很容易地复用ConcreteStrategyA的实现了。
- Strategy模式很State模式也有相似之处,但是State模式注重的对象在不同的状态下不同的操作。两者之间的区别就是State模式中具体实现类中有一个指向Context的引用,而Strategy模式则没有。
3. State模式
a)结构图
状态不同,对输入有不同的响应(状态转移)。通常我们在实现这类系统会使用到很多的Switch/Case语句,Case某种状态,发生什么动作,Case另外一种状态,则发生另外一种状态。但是这种实现方式至少有以下两个问题:
- 当状态数目不是很多的时候,Switch/Case可能可以搞定。但是当状态数目很多的时候(实际系统中也正是如此),维护一大组的Switch/Case语句将是一件异常困难并且容易出错的事情。
- 状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。
在State模式中我们将状态逻辑和动作实现进行分离。当一个操作中要维护大量的case分支语句,并且这些分支依赖于对象的状态。State模式将每一个分支都封装到独立的类中。
b)实例
//main.cpp
#include "Context.h"
#include "State.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[]) {
State* st = new ConcreteStateA();
Context* con = new Context(st);
con->OperationChangState();
con->OperationChangState();
con->OperationChangState();
//连续3次调用了Context的OprationInterface()因为每次调用后状态都会改变(A-B-A),因此该动作随着Context的状态的转变而获得了不同的结果。
if (con != NULL) delete con;
if (st != NULL) st = NULL;
return 0;
}
//context.cpp
#include "Context.h"
#include "State.h"
class State;
class Context
{
public:
Context::Context() {
}
Context::Context(State* state) {
this->_state = state;
}
Context::~Context() {
delete _state;
}
void Context::OperationInterface() {
_state->OperationInterface(this);
}
bool Context::ChangeState(State* state) { ///_state->ChangeState(this,state); 状态改变的动作实现是在context里面的
this->_state = state;
return true;
}
void Context::OperationChangState() {
_state->OperationChangeState(this);
}
private:
friend class State; //表明在State类中可以访问Context类的private字段
private:
State* _state;
};
//state.h
#ifndef _STATE_H_ #define _STATE_H_
class Context; //前置声明
class State {
public:
State(){}
virtual ~State(){}
virtual void OperationInterface(Context* ) = 0;
virtual void OperationChangeState(Context*) = 0;
protected:
bool ChangeState(Context* con,State* st){
con->ChangeState(st); //state里调用Context里面的操作,真正执行状态转换
return true;
}
};
class ConcreteStateA:public State {
public:
ConcreteStateA();
virtual ~ConcreteStateA();
virtual void OperationInterface(Context* ){ cout<<"ConcreteStateA::OperationInterface ......"<<endl; }
virtual void OperationChangeState(Context* con)
{
OperationInterface(con);
this->ChangeState(con,new ConcreteStateB()); //状态逻辑A-B
};
class ConcreteStateB:public State {
public:
ConcreteStateB();
virtual ~ConcreteStateB();
virtual void OperationInterface(Context* ){ cout<<"ConcreteStateB::OperationInterface ......"<<endl; }
virtual void OperationChangeState(Context* con)
{
OperationInterface(con); //当前状态操作
this->ChangeState(con,new ConcreteStateA()); //状态逻辑B-A
}
};
c)讨论
- State及其子类中的操作都将Context*传入作为参数,其主要目的是State类可以通过这个指针调用Context中的方法(在本示例代码中没有体现)。这也是State模式和Strategy模式的最大区别所在。
- State模式和Strategy模式又很大程度上的相似:它们都有一个Context类,都是通过委托(组合)给一个具有多个派生类的多态基类实现Context的算法逻辑。两者最大的差别就是State模式中派生类持有指向Context对象的引用,并通过这个引用调用Context中的方法,但在Strategy模式中就没有这种情况。因此可以说一个State实例同样是Strategy模式的一个实例,反之却不成立。
- State模式主要是要适应对象对于状态改变时的不同处理策略的实现,而Strategy则主要是具体算法和实现接口的解耦(coupling),Strategy模式中并没有状态的概念(虽然很多时候有可以被看作是状态的概念),并且更加不关心状态的改变了。
- State模式很好地实现了对象的状态逻辑和动作实现的分离,状态逻辑分布在State的派生类中实现,而动作实现则可以放在Context类中实现(这也是为什么State派生类需要拥有一个指向Context的指针)。这使得两者的变化相互独立,改变State的状态逻辑可以很容易复用Context的动作,也可以在不影响State派生类的前提下创建Context的子类来更改或替换动作实现。
- State模式问题主要是逻辑分散化,状态逻辑分布到了很多的State的子类中,很难看到整个的状态逻辑图,这也带来了代码的维护问题。
- 运行时根据状态改变行为,逻辑在state具体实现中,消除了对if/else的依赖
4. Observer模式
a)结构图
Observer模式应该可以说是应用最多、影响最广的模式之一。Observer模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变。
最常见的一个例子就是:对同一组数据进行统计分析时候,我们希望能够提供多种形式的表示(例如以表格进行统计显示、柱状图统计显示、百分比统计显示等)。这些表示都依赖于同一组数据,我们当然需要当数据改变的时候,所有的统计的显示都能够同时改变。
这里的目标Subject提供依赖于它的观察者Observer的注册(Attach)和注销(Detach)操作,并且提供了使得依赖于它的所有观察者同步的操作(Notify)。观察者Observer则提供一个Update操作,注意这里的Observer的Update操作延迟到Subject对象发出Notify通知所有Observer进行修改(调用Update)。
b)实例
客户调用
#include "concrete_subject.h"
#include "concrete_observer.h"
int main()
{
// 创建主题、观察者
ConcreteSubject *pSubject = new ConcreteSubject();
IObserver *pObserver1 = new ConcreteObserver("Jack Ma");
IObserver *pObserver2 = new ConcreteObserver("Pony");
// 注册观察者
pSubject->Attach(pObserver1);
pSubject->Attach(pObserver2);
// 更改价格,并通知观察者
pSubject->SetPrice(12.5);
pSubject->Notify();
// 注销观察者
pSubject->Detach(pObserver2);
// 再次更改状态,并通知观察者
pSubject->SetPrice(15.0);
pSubject->Notify();
SAFE_DELETE(pObserver1);
SAFE_DELETE(pObserver2);
SAFE_DELETE(pSubject);
getchar();
return 0;
}
主题
#ifndef SUBJECT_H
#define SUBJECT_H
class IObserver;
// 抽象主题
class ISubject
{
public:
virtual void Attach(IObserver *) = 0; // 注册观察者
virtual void Detach(IObserver *) = 0; // 注销观察者
virtual void Notify() = 0; // 通知观察者
};
#endif // SUBJECT_H
//**********************************************************************************************************//
#ifndef CONCRETE_SUBJECT_H
#define CONCRETE_SUBJECT_H
#include "subject.h"
#include "observer.h"
#include <iostream>
#include <list>
using namespace std;
// 具体主题
class ConcreteSubject : public ISubject
{
public:
ConcreteSubject() { m_fPrice = 10.0; }
void SetPrice(float price) {
m_fPrice = price;
}
void Attach(IObserver *observer) {
m_observers.push_back(observer);
}
void Detach(IObserver *observer) {
m_observers.remove(observer);
}
void Notify() {
list<IObserver *>::iterator it = m_observers.begin();
while (it != m_observers.end()) {
(*it)->Update(m_fPrice);
++it;
}
}
private:
list<IObserver *> m_observers; // 观察者列表
float m_fPrice; // 价格
};
#endif // CONCRETE_SUBJECT_H
观察者
#ifndef OBSERVER_H
#define OBSERVER_H
// 抽象观察者
class IObserver
{
public:
virtual void Update(float price) = 0; // 更新价格
};
#endif // OBSERVER_H
#ifndef CONCRETE_OBSERVER_H
#define CONCRETE_OBSERVER_H
#include "observer.h"
#include <iostream>
#include <string>
using namespace std;
// 具体观察者
class ConcreteObserver : public IObserver
{
public:
ConcreteObserver(string name) { m_strName = name; }
void Update(float price) {
cout << m_strName << " - price: " << price << "\n";
}
private:
string m_strName; // 名字
};
#endif // CONCRETE_OBSERVER_H
c)讨论
Observer模式也称为发布-订阅(publish-subscribe),目标就是通知的发布者,观察者则是通知的订阅者(接受通知)。
5. Memento模式
a)结构图
Memento模式的关键就是要在不破坏封装的前提下,捕获并保存一个类的内部状态,这样就可以利用该保存的状态实施恢复操作。
caretaker有一个Memento的指针容器
b)实例
#include "originator.h"
#include "care_taker.h"
int main()
{
Life *life = new Life();
PandoraBox *box = new PandoraBox(life); //管理Memento的类
// 设置并保存一个历史时间
life->SetDateTime("2000/10/01 00:00:00");
box->Save(); //备份一个时间
// 设置并保存一个历史时间
life->SetDateTime("2010/10/01 00:00:00");
box->Save(); //又备份一个时间
// 设置一个历史时间
life->SetDateTime("2018/10/01 00:00:00");
// 穿越
box->Undo();
std::cout << "Actual date time is " << life->GetDateTime() << std::endl;
// 再次穿越
box->Undo(); //恢复到上一时刻
std::cout << "Actual date time is " << life->GetDateTime() << std::endl;
SAFE_DELETE(life);
SAFE_DELETE(box);
getchar();
return 0;
}
发起人:我要备份的对象,负责创建Memtnto,记录当前内部状态,并可以通过Memento恢复状态
#ifndef ORIGINATOR_H
#define ORIGINATOR_H
#include "memento.h"
#include <iostream>
#include <string>
// 一生
class Life
{
public:
void SetDateTime(std::string dt) {
std::cout << "Set date time to " << dt << std::endl;
m_dateTime = dt;
}
// 仅用于打印
std::string GetDateTime() {
return m_dateTime;
}
// 恢复日期时间
void SetMemento(DateTime *dt) {
m_dateTime = dt->GetDateTime(); //是memento的友元,所以可以访问
}
// 创建日期时间
DateTime *CreateMemento() {
return new DateTime(m_dateTime);
}
private:
std::string m_dateTime;
};
#endif // ORIGINATOR_H
Memento,我要备份的内容
#ifndef MEMENTO_H
#define MEMENTO_H
#include <iostream>
#include <string>
// 日期时间
class DateTime
{
private:
//这是最关键的地方,将Originator为friend类,可以访问内部信息,但是其他类不能访问
friend class Life;
DateTime(std::string dt)
: m_dateTime(dt) {}
void SetDateTime(std::string dt) {
m_dateTime = dt;
}
std::string GetDateTime() {
return m_dateTime;
}
std::string m_dateTime;
};
#endif // MEMENTO_H
管理者:管理Memento,但不能对Memento的内容进行访问
#ifndef CARE_TAKER_H
#define CARE_TAKER_H
#include "originator.h"
#include <iostream>
#include <vector>
// 月光宝盒
class PandoraBox
{
public:
PandoraBox(Life *life)
: m_pLife(life) {}
~PandoraBox() {
for (int i = 0; i < m_history.size(); i++) {
delete m_history.at(i);
}
m_history.clear();
}
// 保存备份
void Save() {
std::cout << "Save ..." << std::endl;;
m_history.push_back(m_pLife->CreateMemento());
}
// 穿越至上一时刻
void Undo() {
std::cout << "Undo ..." << std::endl;;
m_pLife->SetMemento(m_history.back());
m_history.pop_back();
}
private:
Life *m_pLife;
std::vector<DateTime *> m_history;
};
#endif // CARE_TAKER_H
c)讨论
- 提供了一种状态恢复机制
- 在Command模式中,Memento模式经常被用来维护可以撤销(Undo)操作的状态。
- Memento模式的关键就是friend class Originator;我们可以看到,Memento的接口都声明为private,而将Originator声明为Memento的友元类。我们将Originator的状态保存在Memento类中,而将Memento接口private起来,也就达到了封装的功效。
6. Mediator模式
a)结构图
实现对象之间的相互通信,提供一个专门处理对象间交互和通信的类
ConcreteColleageA和ConcreteColleageB之间相互通信,通过Colleague维护的一个Mediator类来实现。
Mediator模式中,每个Colleague维护一个Mediator,当要进行交互,例如图中ConcreteColleagueA和ConcreteColleagueB之间的交互就可以通过ConcreteMediator提供的DoActionFromAtoB来处理,ConcreteColleagueA和ConcreteColleagueB不必维护对各自的引用,甚至它们也不知道各个的存在。Mediator通过这种方式将多对多的通信简化为了一(Mediator)对多(Colleague)的通信。
b)实例
#include "concrete_colleague.h"
#include "concrete_mediator.h"
int main()
{
// 房东
IColleague *landlord = new ConcreteColleague("Tom");
// 租客
IColleague *jerry = new ConcreteColleague("Jerry");
IColleague *tuffy = new ConcreteColleague("Tuffy");
// 中介者 - 添加租客
ConcreteMediator mediator;
mediator.registerColleague(jerry);
mediator.registerColleague(tuffy);
// 房东通过中介将消息发送出去,不需要知道租客信息
landlord->sendMessage(mediator, "Xi'erqi, two bedroom house, 6000/month.");
SAFE_DELETE(jerry);
SAFE_DELETE(tuffy);
getchar();
return 0;
}
中介者
#pragma once
#include "colleague.h"
#include <list>
class IColleague;
// 抽象中介者
class IMediator
{
public:
// 注册参与者
virtual void registerColleague(IColleague* colleague) { m_colleagues.emplace_back(colleague); }
const std::list<IColleague*>& getColleagues() const { return m_colleagues; }
// 将发送者的消息发送给所有参与者
virtual void distributeMessage(const IColleague* sender, const std::string& message) const = 0;
private:
std::list<IColleague*> m_colleagues;
};
#ifndef CONCRETE_MEDIATOR_H
#define CONCRETE_MEDIATOR_H
#include "mediator.h"
// 具体中介者
class ConcreteMediator : public IMediator
{
public:
// 将发送者的消息发送给所有参与者(但不包括发送者自己)
virtual void distributeMessage(const IColleague* sender, const std::string& message) const override {
for (const IColleague* c : getColleagues())
if (c != sender) // 不要将消息发送给自己
c->receiveMessage(sender, message);
}
};
#endif // CONCRETE_MEDIATOR_H
通信对象
#pragma once
#include "mediator.h"
#include <string>
class IMediator;
// 抽象同事类
class IColleague
{
public:
IColleague(const std::string& name) : m_strName(name) {}
std::string getName() const { return m_strName; }
// 通过中介者,将自己的消息发布出去
virtual void sendMessage(const IMediator& mediator, const std::string& message) const = 0;
// 接收来自发送者的消息
virtual void receiveMessage(const IColleague* sender, const std::string& message) const = 0;
private:
std::string m_strName;
};
#ifndef CONCRETE_COLLEAGUE_H
#define CONCRETE_COLLEAGUE_H
#include "colleague.h"
#include <iostream>
// 具体同事类
class ConcreteColleague : public IColleague
{
public:
using IColleague::IColleague;
// 通过中介者,将自己的消息发布出去
virtual void sendMessage(const IMediator& mediator, const std::string& message) const override {
mediator.distributeMessage(this, message);
}
private:
// 接收来自发送者的消息
virtual void receiveMessage(const IColleague* sender, const std::string& message) const override {
std::cout << getName() << " received the message from "
<< sender->getName() << ": " << message << std::endl;
}
};
#endif // CONCRETE_COLLEAGUE_H
c)讨论
- Mediator模式是一种很有用并且很常用的模式,它通过将对象间的通信封装到一个类中,将多对多的通信转化为一对多的通信,降低了系统的复杂性。
- Mediator还获得系统解耦的特性,通过Mediator,各个Colleague就不必维护各自通信的对象和通信协议,降低了系统的耦合性,Mediator和各个Colleague就可以相互独立地修改了。
- ,两个Colleague对象并不知道它交互的对象,并且也不是显示地处理交互过程,这一切都是通过Mediator对象完成的。Mediator模式还有一个很显著额特点就是将控制集中,集中的优点就是便于管理。
7. Command模式
a)结构图
Command模式通过将请求封装到一个对象(Command)中,并将请求的接受者存放到具体的ConcreteCommand类中(Receiver)中,从而实现调用操作的对象和操作的具体实现者之间的解耦。
将请求的接收者(处理者)放到Command的具体子类ConcreteCommand中,当请求到来时(Invoker发出Invoke消息激活Command对象),ConcreteCommand将处理请求交给Receiver对象进行处理。
b)实例
#include "Command.h"
#include "Invoker.h"
#include "Reciever.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
Reciever* rev = new Reciever(); //行为执行者,图像处理,反转、旋转等等操作
Command* cmd = new ConcreteCommand(rev); //将执行者和请求者解耦
Invoker* inv = new Invoker(cmd); //行为请求者,即用户
inv->Invoke();
return 0;
}
#ifndef _RECIEVER_H_
#define _RECIEVER_H_
class Reciever {
public:
Reciever();
~Reciever();
void Action(){
std::cout << "Reciever action......." << std::endl;
}
};
#endif //~_RECIEVER_H_
#ifndef _INVOKER_H_
#define _INVOKER_H_
class Command;
class Invoker {
public:
Invoker(Command* cmd){
_cmd = cmd;
}
~Invoker(){
delete _cmd;
}
void Invoke(){
_cmd->Excute();
}
protected:
private:
Command* _cmd;
};
#endif //~_INVOKER_H_
Command
#ifndef _COMMAND_H_
#define _COMMAND_H_
class Reciever;
class Command {
public:
virtual ~Command();
virtual void Excute() = 0;
protected:
Command();
private:
};
class ConcreteCommand: public Command {
public:
ConcreteCommand(Reciever* rev) {
this->_rev = rev;
}
~ConcreteCommand() {
delete this->_rev;
}
void Excute() {
_rev->Action();
std::cout << "ConcreteCommand..." << std::endl;
}
protected:
private:
Reciever* _rev;
};
#endif //~_COMMAND_H_
c)讨论
- Command模式关键就是提供一个抽象的Command类,并将执行操作封装到Command类接口中,Command类中一般就是只是一些接口的集合,并不包含任何的数据属性
- Command模式将调用操作的对象和知道如何实现该操作的对象解耦。在上面Command的结构图中,Invoker对象根本就不知道具体的是那个对象在处理Excute操作(当然要知道是Command类别的对象,也仅此而已)。
- 在Command要增加新的处理操作对象很容易,我们可以通过创建新的继承自Command的子类来实现这一点。
- Command模式可以和Memento模式结合起来,支持取消的操作。
8. Visitor模式
a)结构图
针对需求变更的情况,常见的方法是给已经设计好的类增加新的方法去实现客户的新需求,但这样就会陷入不停打补丁的魔咒,Visitor模式提供了一种解决方案,将更新封装到一个类中,并由带更改的类提供一类更新类的封装接口。这样就可以在不破坏类的前提下,增加新的操作。
把要访问的元素和访问者分开。
b)实例
Client
#include "concrete_visitor.h"
#include "object_structure.h"
int main()
{
City *city = new City();
// 景点 - 钟楼、兵马俑
IPlace *bellTower = new BellTower();
IPlace *warriors = new TerracottaWarriors();
// 访问者 - 游客、清洁工
IVisitor *tourist = new Tourist();
IVisitor *cleaner = new Cleaner();
// 添加景点
city->Attach(bellTower);
city->Attach(warriors);
// 接受访问
city->Accept(tourist);
city->Accept(cleaner);
SAFE_DELETE(cleaner);
SAFE_DELETE(tourist);
SAFE_DELETE(warriors);
SAFE_DELETE(bellTower);
SAFE_DELETE(city);
getchar();
return 0;
}
Visitor
#ifndef VISITOR_H
#define VISITOR_H
class BellTower;
class TerracottaWarriors;
// ทรฮสี฿
class IVisitor
{
public:
virtual ~IVisitor() {}
virtual void Visit(BellTower *) = 0;
virtual void Visit(TerracottaWarriors *) = 0;
};
#endif // VISITOR_H
#ifndef CONCRETE_VISITOR_H
#define CONCRETE_VISITOR_H
#include "visitor.h"
#include "concrete_element.h"
// 游客
class Tourist : public IVisitor
{
public:
virtual void Visit(BellTower *) override {
std::cout << "I'm visiting the Bell Tower!" << std::endl;
}
virtual void Visit(TerracottaWarriors *) override {
std::cout << "I'm visiting the Terracotta Warriors!" << std::endl;
}
};
// 清洁工
class Cleaner : public IVisitor
{
public:
virtual void Visit(BellTower *) override {
std::cout << "I'm cleaning up the garbage of Bell Tower!" << std::endl;
}
virtual void Visit(TerracottaWarriors *) override {
std::cout << "I'm cleaning up the garbage of Terracotta Warriors!" << std::endl;
}
};
#endif // CONCRETE_VISITOR_H
Element
#ifndef ELEMENT_H
#define ELEMENT_H
class IVisitor;
// хпий
class IPlace
{
public:
virtual ~IPlace() {}
virtual void Accept(IVisitor *visitor) = 0;
};
#endif // ELEMENT_H
#ifndef CONCRETE_ELEMENT_H
#define CONCRETE_ELEMENT_H
#include "element.h"
#include "visitor.h"
#include <iostream>
// 钟楼
class BellTower : public IPlace
{
public:
virtual void Accept(IVisitor *visitor) override {
std::cout << "Bell Tower is accepting visitor." << std::endl;
visitor->Visit(this);
}
};
// 兵马俑
class TerracottaWarriors : public IPlace
{
public:
virtual void Accept(IVisitor *visitor) override {
std::cout << "Terracotta Warriors is accepting visitor." << std::endl;
visitor->Visit(this);
}
};
#endif // CONCRETE_ELEMENT_H
上层接口,枚举所有元素,并且为每个元素添加访问者
#ifndef OBJECT_STRUCTURE_H
#define OBJECT_STRUCTURE_H
#include "element.h"
#include <list>
// 城市(西安)
class City
{
public:
//添加景点
void Attach(IPlace *place) {
m_places.push_back(place);
}
void Detach(IPlace *place) {
m_places.remove(place);
}
void Accept(IVisitor *visitor) {
// 为每一个 element 设置 visitor,进行对应的操作
for (std::list<IPlace*>::iterator it = m_places.begin(); it != m_places.end(); ++it) {
(*it)->Accept(visitor);
}
}
private:
std::list<IPlace *> m_places;
};
#endif // OBJECT_STRUCTURE_H
c)讨论
- 增加新的访问者很方便,增加一个新的访问者类就好了
- 将有关Element的访问行为封装到一个访问者对象当中,而不是分散在元素类之中
- Visitor模式可以使得Element在不修改自己的同时增加新的操作,但是这也带来了至少以下的两个显著问题:
- 破坏了封装性。Visitor模式要求Visitor可以从外部修改Element对象的状态,这一般通过两个方式来实现:a)Element提供足够的public接口,使得Visitor可以通过调用这些接口达到修改Element状态的目的;b)Element暴露更多的细节给Visitor,或者让Element提供public的实现给Visitor(当然也给了系统中其他的对象),或者将Visitor声明为Element的friend类,仅将细节暴露给Visitor。
- ConcreteElement的扩展很困难:每增加一个Element的子类,就要修改Visitor的接口,使得可以提供给这个新增加的子类的访问机制。
9. Chain of Responsibility模式
a)结构图
将可能处理一个请求的对象链接成一个链,并将请求在这个链上传递,直到有对象处理该请求。
Chain of Responsibility模式中ConcreteHandler将自己的后继对象(向下传递消息的对象)记录在自己的后继表中,当一个请求到来时,ConcreteHandler会先检查看自己有没有匹配的处理程序,如果有就自己处理,否则传递给它的后继。
b)实例
Client
#include "concrete_handler.h"
int main()
{
IHandler *manager = new Manager();
IHandler *director = new Director();
IHandler *ceo = new CEO();
// 职责链:经理 -> 总监 -> 总裁
manager->SetSuccessor(director);
director->SetSuccessor(ceo);
manager->HandleRequest(1);
manager->HandleRequest(2);
manager->HandleRequest(5);
manager->HandleRequest(10);
SAFE_DELETE(manager);
SAFE_DELETE(director);
SAFE_DELETE(ceo);
getchar();
return 0;
}
#ifndef HANDLER_H
#define HANDLER_H
#include <iostream>
// 抽象处理者
class IHandler
{
public:
IHandler() { m_pSuccessor = NULL; }
virtual ~IHandler() {}
void SetSuccessor(IHandler *successor) { m_pSuccessor = successor; }
virtual void HandleRequest(float days) = 0;
protected:
IHandler *m_pSuccessor; // 后继者
};
#endif // HANDLER_H
#ifndef CONCRETE_HANDLER_H
#define CONCRETE_HANDLER_H
#include "handler.h"
// 经理
class Manager : public IHandler
{
public:
Manager() {}
~Manager() {}
virtual void HandleRequest(float days) override {
if (days <= 1) {
std::cout << "Manager 批准了 " << days << " 天假" << std::endl;
}
else {
m_pSuccessor->HandleRequest(days);
}
}
};
// 总监
class Director : public IHandler
{
public:
Director() {}
~Director() {}
virtual void HandleRequest(float days) override {
if (days <= 3) {
std::cout << "Director 批准了 " << days << " 天假" << std::endl;
}
else {
m_pSuccessor->HandleRequest(days);
}
}
};
// 总裁
class CEO : public IHandler
{
public:
CEO() {}
~CEO() {}
virtual void HandleRequest(float days) override {
if (days <= 7) {
std::cout << "CEO 批准了 " << days << " 天假" << std::endl;
}
else {
std::cout << "给你放长假,以后不用来上班啦!" << std::endl;
}
}
};
#endif // CONCRETE_HANDLER_H
c)讨论
- Chain of Responsibility模式的最大的一个有点就是给系统降低了耦合性,请求的发送者完全不必知道该请求会被哪个应答对象处理,极大地降低了系统的耦合性。
- 增加新的处理者很方便,后面加个后继就可以
- 不足 在于很难保证请求一定被处理
10. Iterator模式
a)结构图
解决对一个聚合对象的遍历问题,将对聚合的遍历封装到一个类中进行,这样就避免了暴露这个聚合对象的内部表示的可能。
就是C++的迭代器使用的模式,就不介绍了
11. Interpreter模式
a)结构图
一些应用提供了内建(Build-In)的脚本或者宏语言来让用户可以定义他们能够在系统中进行的操作。Interpreter模式的目的就是使用一个解释器为用户提供一个一门定义语言的语法表示的解释器,然后通过这个解释器来解释语言中的句子。
Interpreter模式中,提供了TerminalExpression和NonterminalExpression两种表达式的解释方式,Context类用于为解释过程提供一些附加的信息(例如全局的信息)。