行为模式
观察者模式,策略模式,命令模式,中介者模式,备忘录模式,模板方法模式,迭代器模式,状态模式,责任链模式,解释器模式,访问者模式
保存/封装 行为/请求 | |
---|---|
Strategy 模式 | 抽象出同一的接口,执行不同的算法 |
Command 模式 | 封装一个请求,从而决定何时、怎样满足请求 |
Visitor 模式 | 某些可作用于一个(组)对象上的操作,但不修改这些对象的类 |
x对多的依赖关系 | |
Observer 模式 | (一对多)对多个对象依赖于另外一个对象,而这些对象又如何保持一致 |
Mediator 模式 | (多对多)对象间怎样交互、和谁交互 |
组件协作 | |
Template 模式 | 对算法中的某些步骤 |
状态变化 | |
State 模式 | 对象的状态 |
Memento 模式 | 对一个对象中哪些私有信息存放在该对象之外,以及在对什么时候进行存储 |
数据结构 | |
Chain of Responsibility 模式 | 满足一个请求的对象链 |
Iterator 模式 | 如何遍历、访问一个聚合的各元素 |
领域问题 | |
Interpreter 模式 | 对一个语言的文法及解释 |
保存/封装 行为/请求
Strategy
结构化设计 分而治之
ifelse switch case中 if else绝对不变,可以用面相对象 抽象来解决。
code
cpp
#include <cstdio>
#include <cstdlib>
class Strategy {
public:
virtual ~Strategy() = default;
virtual void excute() = 0;
};
class Strategy_allin : public Strategy {
public:
void excute() {
printf("塔塔开!\n");
}
};
class Strategy_giveup : public Strategy {
public:
void excute() {
printf("点了点了点了.\n");
}
};
class ChasingGame {
public:
void play() {
if (this->mStrategy) {
mStrategy->excute();
} else {
printf("挂机,等着输.\n");
}
}
void set_strategy(Strategy* strategy) {
if (this->mStrategy) {
delete this->mStrategy;
}
this->mStrategy = strategy;
}
Strategy* mStrategy = nullptr;
};
int main() {
ChasingGame* mGame = new ChasingGame;
mGame->play();
Strategy* mStrategy = new Strategy_allin;
mGame->set_strategy(mStrategy);
mGame->play();
mStrategy = new Strategy_giveup;
mGame->set_strategy(mStrategy);
mGame->play();
return 1;
}
command (保存)
将 请求/行为 封装成对象。
基类 Command 虚方法 excute,子类继承后实现excute方法。到此为止的话 和策略模式特别类似,但区别是 存在一个容器(复合命令对象)存放cmd对象,执行其中所有的命令。
-
策略模式,往往需要工厂模式创建一个对象 执行该对象的方法。
-
cmd模式,一般自行创建cmd对象,将请求放入队列中。怎么执行这个请求,是队列决定的
函数对象和cmd模式存在相似。
-
函数对象
-
执行函数对象是通过重载()操作符,
-
函数对象以函数签名来定义行为接口规范,
-
编译时绑定(泛型编程 使用模板,编译时解析类型)
更灵活,性能更高。
-
-
cmd模式
- 以面相对象的 "接口 实现"来定义行为接口规范,更严格
- 有性能损失
- 运行时绑定 > 编译时绑定
cpp
#include <cstdio>
#include <cstdlib>
#include <vector>
class Command {
public:
virtual ~Command() = default;
virtual void excute() = 0;
};
class Command_EatApple : public Command {
public:
void excute() {
printf("eat an Apple\n");
}
};
class Command_DrinkTea : public Command {
public:
void excute() {
printf("drink a cup of tea\n");
}
};
class CmdQueue {
public:
void enqueueCmd(Command* cmd) {
this->cmds.push_back(cmd);
}
void handleCmd() {
for (auto cmd : this->cmds) {
cmd->excute();
}
cmds.clear();
}
std::vector<Command*> cmds;
};
int main() {
CmdQueue* mQueue = new CmdQueue;
Command_DrinkTea* cmd1 = new Command_DrinkTea;
Command_EatApple* cmd2 = new Command_EatApple;
mQueue->enqueueCmd(cmd1);
mQueue->enqueueCmd(cmd2);
printf("容器决定什么时候执行cmd");
mQueue->handleCmd();
delete cmd1;
delete cmd2;
delete mQueue;
return 0;
}
visitor (升维)
概念
对 Strategy模式 的维度提升------
原先,Strategy :
- 实现处 基类定义 子类实现,一个方法
excute
。 - 使用处 调用。
后来,Command:更进一步
- 将 Strategy 视为一个可移动的对象
- 使用处决定 执行/不执行 什么时候执行 其中的
excute
方法。
**现在,visitor :**作用与对象结构的二次辨析(dispatch)
- 实现处同时实现多个不同的动作
- 不同的使用处,选择执行其中一个动作
体现在使用上:
-
实现处 visitor 类
- 基类:要求各个方法的接口足够稳定
- 定义若干虚函数
actionA, actionB ...
- 定义若干虚函数
- 子类:
- 实现虚函数
actionA, actionB ...
- 实现虚函数
- 基类:要求各个方法的接口足够稳定
-
使用处 element类:层次结构稳定,其中面临的操作面临频繁的变化。
-
基类 element:以下接口稳定
- 虚函数
action
,不同的子类对其进行实现
否则一旦基类element添加新的虚函数,就需要修改所有子类。
- 方法accept(&visitor)
接收&保存一个 visitor 对象(不用保存若干不同的 Strategy/Command 对象------ all in one)
- 虚函数
-
子类:要求数量稳定
- 实现接口
action
- 实现的方法是:调用 所保存的 visitor 对象的不同
actionX
。例如- elementA 的方法action执行visitor.actionA
- elementB 的方法action执行visitor.actionB
- 实现接口
-
code
cpp
// 作用与对象结构的二次辨析
#include <cstdio>
class Visitor {
public:
virtual ~Visitor() = default;
virtual void action_lunch() = 0;
virtual void action_dinner() = 0;
};
class Visitor_Shanghai : public Visitor {
public:
virtual void action_lunch() {
printf("山西午餐吃炸酱面\n");
}
virtual void action_dinner() {
printf("山西晚餐吃刀削面\n");
}
};
class Visitor_ShanXi : public Visitor {
public:
virtual void action_lunch() {
printf("上海午餐吃酱鸭子\n");
}
virtual void action_dinner() {
printf("上海晚餐吃剩下的酱鸭子\n");
}
};
class Element {
public:
virtual ~Element() = default;
virtual void eat_meal() = 0;
virtual void accept(Visitor* visitor) {
mVisitor = visitor;
}
Visitor* mVisitor;
};
class Element_Lunch : public Element {
public:
virtual void eat_meal() {
if (mVisitor) {
mVisitor->action_lunch();
}
}
};
class Element_dinner : public Element {
public:
virtual void eat_meal() {
if (mVisitor) {
mVisitor->action_dinner();
}
}
};
int main() {
Visitor* mShanxi = new Visitor_ShanXi;
Visitor* mShanghai = new Visitor_Shanghai;
Element* meal = new Element_Lunch;
meal->accept(mShanghai);
meal->eat_meal();
delete meal;
meal = new Element_dinner;
meal->accept(mShanxi);
meal->eat_meal();
delete meal;
delete mShanghai;
delete mShanxi;
return 1;
}
x对多的依赖关系
Observer 观察者模式(单向)
Observer 模式要解决的问题为:
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变 "一"变化的时候,所有依赖于这个"一"的多对象都得到通知并被自动更新。
Subject 提供
-
依赖于它的观察者 Observer 的
- 注册(registerObserver)
- 注销(remove)操作,
-
使依赖于它的所有观察者同步的操作(notifyObserver),
观察者 Observer 提供
- Update 操作
- 注意这里的 Observer 的 Update 操作并不在 Observer 改变了 Subject 目标状态的时候就对自己进行更新,这个更新操作要延迟到 Subject 对象发出 Notify 通知所有Observer 进行修改(调用 Update)。
cpp
#include <cstdio>
#include <iostream>
#include <string>
#include <list>
class Observer;
class Subscriber;
class Subject {
public:
virtual ~Subject() = default;
virtual void registObserver(Observer*) = 0;
virtual void removeObserver(Observer*) = 0;
virtual void notifyObserver() = 0;
std::list<Observer*> mObservers;
};
class Subscriber {
public:
Subscriber(std::string name) : name(name){};
std::string name;
void watchVideo() {
printf("%s start watching . \n", this->name.c_str());
}
};
class Observer {
public:
void addSubscriber(Subscriber*);
void removeSubscriber(Subscriber*);
void notifySubscriber();
std::list<Subscriber*> mSubcribers;
};
class Subject_Bilibili : public Subject {
public:
virtual void registObserver(Observer* observer) {
this->mObservers.push_back(observer);
};
virtual void removeObserver(Observer* observer) {
this->mObservers.remove(observer);
};
virtual void notifyObserver() {
printf("bilibili update! \n");
for (auto observer : this->mObservers) {
observer->notifySubscriber();
}
}
};
void Observer::addSubscriber(Subscriber* subscriber) {
this->mSubcribers.push_back(subscriber);
}
void Observer::removeSubscriber(Subscriber* subscriber) {
this->mSubcribers.remove(subscriber);
}
void Observer::notifySubscriber() {
printf("好的,这就通知\n");
for (auto subscriber : this->mSubcribers) {
subscriber->watchVideo();
}
}
int main() {
Subject* biblibili = new Subject_Bilibili;
Observer* mObserver = new Observer;
Subscriber* A = new Subscriber("张三");
Subscriber* B = new Subscriber("李四");
Subscriber* C = new Subscriber("王五");
biblibili->registObserver(mObserver);
mObserver->addSubscriber(A);
mObserver->addSubscriber(B);
mObserver->addSubscriber(C);
mObserver->removeSubscriber(C);
biblibili->notifyObserver();
return 1;
}
Mediator 中介模式(双向/多向)
Mediator 解耦系统内多个类之间需要大量密集复杂的相互交互,对他们进行集中管理。
- 依赖倒置原则 在多对象模型中的体现
- 类似交换机,要求 通信规范
- 实现
- 让这些类继承相同的接口/成为某一基类的子类
- 子类都持有中介的指针
- 中介持有所有子类的指针
基类成员 基类::成员名,与子类成员进行区分。
cpp
#include <cstdio>
#include <iostream>
#include <string>
#include <list>
class Mediator;
class Client {
public:
virtual ~Client() = default;
void registMediator(Mediator* mediator);
void callSomeTeam(int type);
virtual void responce() = 0;
Mediator* mMediator;
int type;
std::string name;
};
class Client_Boss : public Client {
public:
virtual ~Client_Boss() = default;
Client_Boss(std::string name, Mediator* mediator) {
Client::name = name;
Client::type = 99;
registMediator(mediator);
}
virtual void responce() {
printf("Boss %s here! what do u want?\n", name.c_str());
}
};
class Client_Engineer : public Client {
public:
virtual ~Client_Engineer() = default;
Client_Engineer(std::string name, Mediator* mediator) {
Client::name = name;
Client::type = 1;
registMediator(mediator);
}
virtual void responce() {
printf("Engineer %s here! how can i help u?\n", name.c_str());
}
};
class Mediator {
public:
void registClient(Client* client) {
this->mClient.push_back(client);
}
void removeClient(Client* client) {
this->mClient.remove(client);
}
void callClient(int type) {
printf("callClient type = %d \n", type);
for (auto client : this->mClient) {
if (client->type == type) {
client->responce();
}
}
}
std::list<Client*> mClient;
};
void Client::callSomeTeam(int type) {
if (this->mMediator)
this->mMediator->callClient(type);
else
printf("unset Mediator\n");
}
void Client::registMediator(Mediator* mediator){
this->mMediator = mediator;
mediator->registClient(this);
}
int main() {
Mediator* mMediator = new Mediator;
Client_Boss* mBoss = new Client_Boss("fajie", mMediator);
Client_Engineer* mEngineer1 = new Client_Engineer("pengyi 1", mMediator);
Client_Engineer* mEngineer2 = new Client_Engineer("pengyi 2", mMediator);
mBoss->callSomeTeam(1);
printf("10mins after\n");
mEngineer1->callSomeTeam(99);
return 1;
}
组件协作
Template 模板模式
对于某一个业务逻辑(算法实现)在不同的对象中有不同的细节实现,但是逻辑(算法)的框架(或通用的应用算法)是相同的。
Template 模式,采用继承的方式实现这一点
-
抽象基类:
定义细节的接口,定义逻辑(算法)框架
-
子类
实现逻辑细节。
在子类实现详细的处理算法时并不会改变算法中的执行次序。
cpp
#include <iostream>
using namespace std;
//做饮料模板
class TemplateDrink {
public:
virtual ~TemplateDrink() = default;
//煮水
virtual void BoildWater() = 0;
//冲泡
virtual void Brew() = 0;
//倒入杯中
virtual void PourInCup() = 0;
//加辅助材料
virtual void AddSomething() = 0;
//模板方法
void Make() {
BoildWater();
Brew();
PourInCup();
AddSomething();
}
};
class Coffee : public TemplateDrink {
virtual void BoildWater() {
cout << "煮纯净水" << endl;
}
virtual void Brew() {
cout << "冲泡咖啡" << endl;
}
virtual void PourInCup() {
cout << "咖啡倒入杯中" << endl;
}
virtual void AddSomething() {
cout << "加牛奶" << endl;
}
};
class Tea :public TemplateDrink {
virtual void BoildWater() {
cout << "煮山泉水" << endl;
}
virtual void Brew() {
cout << "冲泡铁观音" << endl;
}
virtual void PourInCup() {
cout << "茶水倒入杯中" << endl;
}
virtual void AddSomething() {
}
};
int main()
{
Tea* tea = new Tea;
tea->Make();
Coffee* coffee = new Coffee;
coffee->Make();
delete tea;
delete coffee;
}
状态变化
State 状态模式
主要解决:Switch/Case 的爆炸
Switch/Case 的缺点
-
当状态数目很多的时候,维护一大组的Switch/Case 语句将是一件异常困难并且容易出错的事情。
-
状态逻辑和动作实现没有分离。
-
动作的实现代码直接写在状态的逻辑当中。
后果是系统的扩展性和维护得不到保证。
概念
和策略模式类似
每个人、事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State)。
状态机
- 不同状态的operation,高度相似,相对固定。
- operation依据某个变量state,发生变化。
优点
State类:枚举可能的状态,
-
可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
-
封装了转换规则。在枚举状态之前需要确定状态种类。
Process:将所有与某个状态有关的行为放到一个类中,
- 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
系统资源
- 通常与单例模式一起使用
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
实现上
State
-
State基类 除虚析构外,还持有
- 一系列operation虚函数
- 看情况
- process指针(使用该指针,更改process中的下一个state)
- state指针(指向下一个状态),(上下文指针)
-
State子类继承State基类
- 重写operarion函数。
Process
-
Process 基类
- 持有State指针
- 一系列operation虚函数。
-
Process 子类
-
重写operation
-
调用State指针的operation
让State更新为下一个状态 state = state->next。
-
code
cpp
// main.cpp
#include <cstdio>
#include <cstdlib>
#include "processImpl.cpp"
#include "stateImpl.cpp"
int main() {
Process* mProcess = new Process(10);
mProcess->buy(101);
mProcess->buy(1);
mProcess->recharge(10);
mProcess->buy(1);
mProcess->recharge(1000);
mProcess->buy(100);
delete mProcess;
return 1;
}
cpp
// process.h
#include <cstdio>
#include <cstdlib>
#include "state.h"
#pragma once
class Process {
public:
Process(int = 10);
void set_state(State* state);
void recharge(int money);
void buy(int price);
int query_money();
int balance;
State* mState = nullptr;
};
cpp
// processImpl.cpp
#include "process.h"
#include "state.h"
#include <cstdio>
#pragma once
Process::Process(int init_money) {
this->balance = init_money;
if (init_money > 0) {
this->set_state(new State_rich);
} else {
this->set_state(new State_poor);
}
}
int Process::query_money() {
return this->balance;
}
void Process::set_state(State* state) {
if (mState)
delete this->mState;
this->mState = state;
this->mState->set_process(this);
}
void Process::recharge(int money) {
bool result = this->mState->recharge(money);
this->balance += money;
if (result) {
printf("尊贵的用户: 充值后,余额 %d\n", this->query_money());
} else {
printf("欠费的用户: 充值后,余额 %d\n", this->query_money());
}
}
void Process::buy(int price) {
if (this->mState->buy(price))
printf("购买成功, 余额 %d\n", this->query_money());
else
printf("购买失败 请充值, 余额 %d\n", this->query_money());
}
cpp
// state.h
#include <cstdio>
#include <cstdlib>
#pragma once
class Process;
class State {
public:
virtual ~State() = default;
void set_process(Process* process) {
this->process = process;
}
Process* get_process() {
return this->process;
}
virtual bool buy(int) = 0;
virtual bool recharge(int) = 0;
Process* process;
};
class State_rich : public State {
public:
bool buy(int) override;
bool recharge(int) override;
};
class State_poor : public State {
public:
bool buy(int) override;
bool recharge(int) override;
};
cpp
// stateImpl.cpp
#include <cstdio>
#include <cstdlib>
#include "state.h"
#include "process.h"
#pragma once
bool State_rich::buy(int price) {
this->process->balance -= price;
int balance = this->process->query_money();
if (balance <= 0) {
this->process->set_state(new State_poor);
}
return true;
}
bool State_rich::recharge(int money) {
int balance = this->process->query_money();
if (money + balance > 0) {
return true;
}
return false;
}
bool State_poor::buy(int price) {
// int balance = this->process->query_money();
return false;
}
bool State_poor::recharge(int money) {
int balance = this->process->query_money();
if (money + balance > 0) {
this->process->set_state(new State_rich);
return true;
}
return false;
}
Memento (过时)
Memento 备忘录模式
信息隐藏条件下,对对象的快照。
原发器,持有state,保存快照时 根据state创建并返回一个memnto对象。恢复时,传入memnto对象,设置 原发器的state对象。 实现对原发器对外的信息隐藏。
现在来看太低级了 java c#等 具有高效成熟容易正确实现的对象序列化支持,有更容易正确实现的序列化方案。
不怎么介绍也没关系
迭代器
从性能角度讲------编译期多态好过运行时多态
-
泛型编程的迭代器 > 面相对象的迭代器
-
虚函数成本过高
但在 java c# php等等语言,不支持编译时多态,仍然在使用运行时多态
责任链模式
运行时的类型判断。一个请求者可能有多个接受者,但最后真正的接受者或者说处理者只有一个。运行时动态添加 修改请求的处理职责。
避免请求的发送者和接受者之间的耦合关系。
数据结构构成的处理模型。链表
行为变化,将组件的行为与组件本身进行解耦
非虚函数,静态函数 地址在编译时绑定的方式。
虚函数是运行时,使用虚函数表指针绑定的。
Interpreter 模式
对一个语言的文法及解释