设计模式 - 行为模式

行为模式

观察者模式,策略模式,命令模式,中介者模式,备忘录模式,模板方法模式,迭代器模式,状态模式,责任链模式,解释器模式,访问者模式

保存/封装 行为/请求
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 模式

对一个语言的文法及解释

相关推荐
计科土狗34 分钟前
前缀和与差分
c++·算法
silver6871 小时前
桥接模式详解
设计模式
思忖小下2 小时前
梳理你的思路(从OOP到架构设计)_设计模式Observer模式
观察者模式·设计模式·eit
午言若2 小时前
MYSQL 架构
c++·mysql
绝无仅有3 小时前
PHP语言laravel框架中基于Redis的异步队列使用实践与原理
后端·面试·架构
羑悻的小杀马特4 小时前
【AIGC篇】畅谈游戏开发设计中AIGC所发挥的不可或缺的作用
c++·人工智能·aigc·游戏开发
闻缺陷则喜何志丹4 小时前
【C++动态规划】1105. 填充书架|2104
c++·算法·动态规划·力扣·高度·最小·书架
ThetaarSofVenice4 小时前
能省一点是一点 - 享元模式(Flyweight Pattern)
java·设计模式·享元模式
记得多喝水o4 小时前
图解设计模式
设计模式
InSighT__4 小时前
设计模式与游戏完美开发(2)
java·游戏·设计模式