(设计模式) (李建忠 C++) 23种设计模式

文章目录

前言

李建忠老师C++设计模式教程

源码资料来自

rhyspang/CPP-Design-Patterns: C++设计模式 (github.com)


  • 组件协作:

    • Template Method
    • Observer / Event
    • Strategy
  • 单一职责:

    • Decorator
    • Bridge
  • 对象创建:

    • Factory Method
    • Abstract Factory
    • Prototype
    • Builder
  • 对象性能:

    • Singleton
    • Flyweight
  • 接口隔离:

    • Façade

    • Proxy

    • Mediator

    • Adapter

  • 状态变化:

    • Memento

    • State

  • 数据结构:

    • Composite

    • Iterator

    • Chain of Resposibility

  • 行为变化:

    • Command

    • Visitor

  • 领域问题:

    • Interpreter

本文的代码,将老师在课程中一些疏忽错误的地方进行了修改

并在每个模式最后加入了个人的小结

组件协作

在这里插入图片描述

模板方法 Template Method

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

有一个固定流程有5步,其中两步是需要随时更改

旧版本由开发人员自己写流程和改动的两步

如何开发的更加合理和提高复用性

版本1

朴素版由应用开发人员写主流程

template1_lib.cpp

//程序库开发人员
class Library {
public:
    void Step1() {
        //...
    }

    void Step3() {
        //...
    }

    void Step5() {
        //...
    }
};

template1_app.cpp

//应用程序开发人员
class Application {
public:
    bool Step2() {
        //...
    }

    void Step4() {
        //...
    }
};

int main() {
    Library lib();
    Application app();

    lib.Step1();

    if (app.Step2()) {
        lib.Step3();
    }

    for (int i = 0; i < 4; i++) {
        app.Step4();
    }

    lib.Step5();
}
版本2

借助继承的虚函数的晚绑定的机制

在程序库中直接写了主流程,应用开发人员仅需要实现具体细节函数即可

template2_lib.cpp

//程序库开发人员
class Library {
public:
    //稳定 template method
    void Run() {
        Step1();

        if (Step2()) {  //支持变化 ==> 虚函数的多态调用
            Step3();
        }

        for (int i = 0; i < 4; i++) {
            Step4();  //支持变化 ==> 虚函数的多态调用
        }

        Step5();
    }
    virtual ~Library() {
    }

protected:
    void Step1() {  //稳定
        //.....
    }
    void Step3() {  //稳定
        //.....
    }
    void Step5() {  //稳定
        //.....
    }

    virtual bool Step2() = 0;  //变化
    virtual void Step4() = 0;  //变化
};

template2_app.cpp

//应用程序开发人员
class Application : public Library {
protected:
    virtual bool Step2() {
        //... 子类重写实现
    }

    virtual void Step4() {
        //... 子类重写实现
    }
};

int main() {
    Library* pLib = new Application();
    lib->Run();

    delete pLib;
}

变化原理

早绑定与晚绑定

版本1是早绑定

版本2是晚绑定

在这里插入图片描述

结构化软件设计流程

在这里插入图片描述

面向对象软件设计流程

在这里插入图片描述

要点总结

在这里插入图片描述

个人小结

这是非常非常常用的一种设计模式

常用到其实我们开发人员几乎每天都在无形中使用

比如:写一个创建一个线程的时候,一般只需要继承线程类

在override run() 然后再调用线程类的start()

这就是一个典型的模板方法

策略模式 Strategy

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

现有一个各种税率计算的类

现在需要添加新国家,如何避免在原代码上大量改动

版本1

if else

enum TaxBase {
    CN_Tax,
    US_Tax,
    DE_Tax,
    FR_Tax  //更改
};

class SalesOrder {
    TaxBase tax;

public:
    double CalculateTax() {
        //...

        if (tax == CN_Tax) {
            // CN***********
        } else if (tax == US_Tax) {
            // US***********
        } else if (tax == DE_Tax) {
            // DE***********
        } else if (tax == FR_Tax) {  //更改
                                     //...
        }

        //....
    }
};
版本2

通过继承抽象类,来单独处理一个情况

软件使用时,动态的调用


SalesOrder涉及到了工厂模式,但不是本文重点

class TaxStrategy {
public:
    virtual double Calculate(const Context& context) = 0;
    virtual ~TaxStrategy() {
    }
};

class CNTax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //***********
    }
};

class USTax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //***********
    }
};

class DETax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //***********
    }
};

//扩展
//*********************************
class FRTax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //.........
    }
};

class SalesOrder {
private:
    TaxStrategy* strategy;

public:
    SalesOrder(StrategyFactory* strategyFactory) {
        this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder() {
        delete this->strategy;
    }

public
    double CalculateTax() {
        //...
        Context context();

        double val = strategy->Calculate(context);  //多态调用
        //...
    }
};

要点总结

在这里插入图片描述

个人小结

看到加if else或switch的并列情况时候就要想到用策略模式

但是在确定if else的可能性的情况下,可以保留

观察者模式 Observer

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

场景

一个文件分割器,需要有一个进度条的展示 (这是一个实现细节)

实现细节非常容易变,如可能不用进度条,改为百分比,点点点或者其他

版本1

直接放一个进度条的对象

FileSplitter1.cpp

class FileSplitter {
    string m_filePath;
    int m_fileNumber;
	// 具体的进度条
    ProgressBar* m_progressBar;

public:
    FileSplitter(const string& filePath, int fileNumber,
                 ProgressBar* progressBar)
        : m_filePath(filePath),
          m_fileNumber(fileNumber),
          m_progressBar(progressBar) {
    }

    void split() {
        // 1.读取大文件

        // 2.分批次向小文件中写入
        for (int i = 0; i < m_fileNumber; i++) {
            //...
            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            m_progressBar->setValue(progressValue);
        }
    }
};

MainForm1.cpp

class MainForm : public Form {
    TextBox* txtFilePath;
    TextBox* txtFileNumber;
    ProgressBar* progressBar;

public:
    void Button1_Click() {
        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());

        FileSplitter splitter(filePath, number, progressBar);

        splitter.split();
    }
};
版本2

抽象出 事件 的本质,是一个通知

FileSplitter2.cpp

class IProgress {
public:
    virtual void DoProgress(float value) = 0;
    virtual ~IProgress() {
    }
};

class FileSplitter {
    string m_filePath;
    int m_fileNumber;
    // 实现器
    // ProgressBar* m_progressBar;
    // 本质是一个通知
    // 紧耦合变松耦合
    IProgress* m_iprogress;

public:
    FileSplitter(const string& filePath, int fileNumber, IProgress* iprogress)
        : m_filePath(filePath), m_fileNumber(fileNumber), m_iprogress(iprogress) {
    }

    void split() {
        // 1.读取大文件

        // 2.分批次向小文件中写入
        for (int i = 0; i < m_fileNumber; i++) {
            //...
            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            onProgress(progressValue);
        }
    }

protected:
    virtual void onProgress(float value) {
        if (m_iprogress != nullptr) {
            // 更新进度条
            m_iprogress->DoProgress(value);
        }
    }
};

MainForm2.cpp

class MainForm : public Form, public IProgress {
    TextBox* txtFilePath;
    TextBox* txtFileNumber;
    ProgressBar* progressBar;

public:
    void Button1_Click() {
        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());

        FileSplitter splitter(filePath, number, this);

        splitter.split();
    }

    virtual void DoProgress(float value) {
        progressBar->setValue(value);
    }
};
版本2

如果是 一对多 一个通知,多个对象接收并操作

FileSplitter3.cpp

class IProgress {
public:
    virtual void DoProgress(float value) = 0;
    virtual ~IProgress() {
    }
};

class FileSplitter {
    string m_filePath;
    int m_fileNumber;

    List<IProgress*> m_iprogressList;  // 抽象通知机制,支持多个观察者

public:
    FileSplitter(const string& filePath, int fileNumber)
        : m_filePath(filePath), m_fileNumber(fileNumber) {
    }

    void split() {
        // 1.读取大文件

        // 2.分批次向小文件中写入
        for (int i = 0; i < m_fileNumber; i++) {
            //...

            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            onProgress(progressValue);  //发送通知
        }
    }

    void addIProgress(IProgress* iprogress) {
        m_iprogressList.push_back(iprogress);
    }

    void removeIProgress(IProgress* iprogress) {
        m_iprogressList.remove(iprogress);
    }

protected:
    virtual void onProgress(float value) {
        List<IProgress*>::iterator itor = m_iprogressList.begin();

        while (itor != m_iprogressList.end()) {
            (*itor)->DoProgress(value);  //更新进度条
            itor++;
        }
    }
};

MainForm3.cpp

class MainForm : public Form, public IProgress {
    TextBox* txtFilePath;
    TextBox* txtFileNumber;

    ProgressBar* progressBar;

public:
    void Button1_Click() {
        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());

        ConsoleNotifier cn;

        FileSplitter splitter(filePath, number);

        splitter.addIProgress(this);  //订阅通知
        splitter.addIProgress(&cn);   //订阅通知

        splitter.split();

        splitter.removeIProgress(this);
    }

    virtual void DoProgress(float value) {
        progressBar->setValue(value);
    }
};

class ConsoleNotifier : public IProgress {
public:
    virtual void DoProgress(float value) {
        cout << ".";
    }
};

要点总结

在这里插入图片描述

个人小结

这个案例的优化中第一反应很容易想到,ProgressBar抽象到一个基类中

但没从触发的角度思考,并且要考虑一对多的拓展性

考虑情景问题一定要多角度分析

单一职责

在这里插入图片描述

装饰模式 Decorator

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

有一个流的基类

拓展出了文件流,网络流,内存流

然后在这些流中进行一些扩展,比如加密操作,缓存操作等等

  • 文件流,网络流,内存流 是主体
  • 加密,缓存 是扩展
代码1

decorator1.cpp

最无脑的操作,直接对文件流,网络流,内存流进行继承,对应加密,缓存等操作

在这里插入图片描述

//业务操作
class Stream {
public:
    virtual char Read(int number) = 0;
    virtual void Seek(int position) = 0;
    virtual void Write(char data) = 0;

    virtual ~Stream() {
    }
};

//主体类
class FileStream : public Stream {
public:
    virtual char Read(int number) {
        //读文件流
    }
    virtual void Seek(int position) {
        //定位文件流
    }
    virtual void Write(char data) {
        //写文件流
    }
};

class NetworkStream : public Stream {
public:
    virtual char Read(int number) {
        //读网络流
    }
    virtual void Seek(int position) {
        //定位网络流
    }
    virtual void Write(char data) {
        //写网络流
    }
};

class MemoryStream : public Stream {
public:
    virtual char Read(int number) {
        //读内存流
    }
    virtual void Seek(int position) {
        //定位内存流
    }
    virtual void Write(char data) {
        //写内存流
    }
};

//扩展操作
class CryptoFileStream : public FileStream {
public:
    virtual char Read(int number) {
        //额外的加密操作...
        FileStream::Read(number);  //读文件流
    }
    virtual void Seek(int position) {
        //额外的加密操作...
        FileStream::Seek(position);  //定位文件流
        //额外的加密操作...
    }
    virtual void Write(byte data) {
        //额外的加密操作...
        FileStream::Write(data);  //写文件流
        //额外的加密操作...
    }
};

class CryptoNetworkStream : public NetworkStream {
public:
    virtual char Read(int number) {
        //额外的加密操作...
        NetworkStream::Read(number);  //读网络流
    }
    virtual void Seek(int position) {
        //额外的加密操作...
        NetworkStream::Seek(position);  //定位网络流
        //额外的加密操作...
    }
    virtual void Write(byte data) {
        //额外的加密操作...
        NetworkStream::Write(data);  //写网络流
        //额外的加密操作...
    }
};

class CryptoMemoryStream : public MemoryStream {
public:
    virtual char Read(int number) {
        //额外的加密操作...
        MemoryStream::Read(number);  //读内存流
    }
    virtual void Seek(int position) {
        //额外的加密操作...
        MemoryStream::Seek(position);  //定位内存流
        //额外的加密操作...
    }
    virtual void Write(byte data) {
        //额外的加密操作...
        MemoryStream::Write(data);  //写内存流
        //额外的加密操作...
    }
};

class BufferedFileStream : public FileStream {
    //...
};

class BufferedNetworkStream : public NetworkStream {
    //...
};

class BufferedMemoryStream : public MemoryStream {
    //...
};

class CryptoBufferedFileStream : public FileStream {
public:
    virtual char Read(int number) {
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Read(number);  //读文件流
    }
    virtual void Seek(int position) {
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Seek(position);  //定位文件流
        //额外的加密操作...
        //额外的缓冲操作...
    }
    virtual void Write(byte data) {
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Write(data);  //写文件流
        //额外的加密操作...
        //额外的缓冲操作...
    }
};

void Process() {
    //编译时装配
    CryptoFileStream *fs1 = new CryptoFileStream();

    BufferedFileStream *fs2 = new BufferedFileStream();

    CryptoBufferedFileStream *fs3 = new CryptoBufferedFileStream();
}
版本2

decorator2.cpp

//业务操作
class Stream {
public:
    virtual char Read(int number) = 0;
    virtual void Seek(int position) = 0;
    virtual void Write(char data) = 0;

    virtual ~Stream() {
    }
};

//主体类
class FileStream : public Stream {
public:
    virtual char Read(int number) {
        //读文件流
    }
    virtual void Seek(int position) {
        //定位文件流
    }
    virtual void Write(char data) {
        //写文件流
    }
};

class NetworkStream : public Stream {
public:
    virtual char Read(int number) {
        //读网络流
    }
    virtual void Seek(int position) {
        //定位网络流
    }
    virtual void Write(char data) {
        //写网络流
    }
};

class MemoryStream : public Stream {
public:
    virtual char Read(int number) {
        //读内存流
    }
    virtual void Seek(int position) {
        //定位内存流
    }
    virtual void Write(char data) {
        //写内存流
    }
};

//扩展操作
// 这里的继承是为了完成接口规范
class CryptoStream : public Stream {
    // 具体实现的发起方
    // 组合引出多态
    Stream* stream;  //...

public:
    // 不能直接实例 Stream
    // 可以实例主体的分支
    // - FileStream
    // - NetworkStream
    // - MemoryStream
    CryptoStream(Stream* stm) : stream(stm) {
    }

    virtual char Read(int number) {
        //额外的加密操作...
        stream->Read(number);  //读文件流
    }
    virtual void Seek(int position) {
        //额外的加密操作...
        stream->Seek(position);  //定位文件流
        //额外的加密操作...
    }
    virtual void Write(byte data) {
        //额外的加密操作...
        stream->Write(data);  //写文件流
        //额外的加密操作...
    }
};

class BufferedStream : public Stream {
    Stream* stream;  //...

public:
    BufferedStream(Stream* stm) : stream(stm) {
    }
    //...
};

void Process() {
    //运行时装配
    FileStream* s1 = new FileStream();

    CryptoStream* s2 = new CryptoStream(s1);
    MemoryStream* s3 = new MemoryStream(s1);
    BufferedStream* s4 = new BufferedStream(s2);
}
版本3

decorator3.cpp

在这里插入图片描述

//业务操作
class Stream {
public:
    virtual char Read(int number) = 0;
    virtual void Seek(int position) = 0;
    virtual void Write(char data) = 0;

    virtual ~Stream() {
    }
};

//主体类
class FileStream : public Stream {
public:
    virtual char Read(int number) {
        //读文件流
    }
    virtual void Seek(int position) {
        //定位文件流
    }
    virtual void Write(char data) {
        //写文件流
    }
};

class NetworkStream : public Stream {
public:
    virtual char Read(int number) {
        //读网络流
    }
    virtual void Seek(int position) {
        //定位网络流
    }
    virtual void Write(char data) {
        //写网络流
    }
};

class MemoryStream : public Stream {
public:
    virtual char Read(int number) {
        //读内存流
    }
    virtual void Seek(int position) {
        //定位内存流
    }
    virtual void Write(char data) {
        //写内存流
    }
};

//扩展操作
// 中间的装饰类
DecoratorStream : public Stream {
protected:
    Stream* stream;  //...

    DecoratorStream(Stream * stm) : stream(stm) {
    }
};

class CryptoStream : public DecoratorStream {
public:
    CryptoStream(Stream* stm) : DecoratorStream(stm) {
    }

    virtual char Read(int number) {
        //额外的加密操作...
        stream->Read(number);  //读文件流
    }
    virtual void Seek(int position) {
        //额外的加密操作...
        stream->Seek(position);  //定位文件流
        //额外的加密操作...
    }
    virtual void Write(byte data) {
        //额外的加密操作...
        stream->Write(data);  //写文件流
        //额外的加密操作...
    }
};

class BufferedStream : public DecoratorStream {
    Stream* stream;  //...

public:
    BufferedStream(Stream* stm) : DecoratorStream(stm) {
    }
    //...
};

void Process() {
    //运行时装配
    FileStream* s1 = new FileStream();

    CryptoStream* s2 = new CryptoStream(s1);
    MemoryStream* s3 = new MemoryStream(s1);
    BufferedStream* s4 = new BufferedStream(s2);
}

要点总结

在这里插入图片描述

个人小结

这种设计模式的样子非常特别,既有继承的类,又有基类成员变量的指针

但是这两者的作用是不一样的

  • 继承 为了实现接口规范
  • 基类指针 为了实现操作

从本样例来看,虽然都是从Stream的延伸,但是

  • FileStream主体的延伸

  • BufferedStream是拓展的操作

两个的分支类型是不一样的,可以理解为一个是主体,一个数属性

既然这样就可以利用多态的指向提高代码的复用性了

桥模式 Bridge

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

有一个通讯模块,在不同平台要具体实现

然后在各自平台再退出不同版本

和装饰模式很像

但是这里的基类抽象方法很多(分类多,即不应该都集中在一个类中)

代码1

bridge1.cpp

// 通信的模块
class Messager {
public:
    virtual void Login(string username, string password) = 0;
    virtual void SendMessage(string message) = 0;
    virtual void SendPicture(Image image) = 0;

    virtual void PlaySound() = 0;
    virtual void DrawShape() = 0;
    virtual void WriteText() = 0;
    virtual void Connect() = 0;

    virtual ~Messager() {
    }
};

//平台实现

class PCMessagerBase : public Messager {
public:
    virtual void PlaySound() {
        //**********
    }
    virtual void DrawShape() {
        //**********
    }
    virtual void WriteText() {
        //**********
    }
    virtual void Connect() {
        //**********
    }
};

class MobileMessagerBase : public Messager {
public:
    virtual void PlaySound() {
        //==========
    }
    virtual void DrawShape() {
        //==========
    }
    virtual void WriteText() {
        //==========
    }
    virtual void Connect() {
        //==========
    }
};

//业务抽象

class PCMessagerLite : public PCMessagerBase {
public:
    virtual void Login(string username, string password) {
        PCMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message) {
        PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image) {
        PCMessagerBase::DrawShape();
        //........
    }
};

class PCMessagerPerfect : public PCMessagerBase {
public:
    virtual void Login(string username, string password) {
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message) {
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image) {
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::DrawShape();
        //........
    }
};

class MobileMessagerLite : public MobileMessagerBase {
public:
    virtual void Login(string username, string password) {
        MobileMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message) {
        MobileMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image) {
        MobileMessagerBase::DrawShape();
        //........
    }
};

class MobileMessagerPerfect : public MobileMessagerBase {
public:
    virtual void Login(string username, string password) {
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message) {
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image) {
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::DrawShape();
        //........
    }
};

// 类的数目
// 1 + n + n*m
void Process() {
    //编译时装配
    Messager *m = new MobileMessagerPerfect();
}
代码2

bridge2.cpp

在这里插入图片描述

class Messager {
protected:
    // 多态指针提升的位置
    MessagerImp* messagerImp;  //...

public:
    virtual void Login(string username, string password) = 0;
    virtual void SendMessage(string message) = 0;
    virtual void SendPicture(Image image) = 0;

    virtual ~Messager() {
    }
};

class MessagerImp {
public:
    virtual void PlaySound() = 0;
    virtual void DrawShape() = 0;
    virtual void WriteText() = 0;
    virtual void Connect() = 0;

    virtual MessagerImp() {
    }
};

//平台实现 n
class PCMessagerImp : public MessagerImp {
public:
    virtual void PlaySound() {
        //**********
    }
    virtual void DrawShape() {
        //**********
    }
    virtual void WriteText() {
        //**********
    }
    virtual void Connect() {
        //**********
    }
};

class MobileMessagerImp : public MessagerImp {
public:
    virtual void PlaySound() {
        //==========
    }
    virtual void DrawShape() {
        //==========
    }
    virtual void WriteText() {
        //==========
    }
    virtual void Connect() {
        //==========
    }
};

//业务抽象 m

//类的数目:1+n+m

class MessagerLite : public Messager {
public:
    MessagerLite(MessagerImp* mImp) : Messager(mImp) {
    }

    virtual void Login(string username, string password) {
        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string message) {
        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image) {
        messagerImp->DrawShape();
        //........
    }
};

class MessagerPerfect : public Messager {
public:
    MessagerPerfect(MessagerImp* mImp) : Messager(mImp) {
    }

    virtual void Login(string username, string password) {
        messagerImp->PlaySound();
        //********
        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string message) {
        messagerImp->PlaySound();
        //********
        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image) {
        messagerImp->PlaySound();
        //********
        messagerImp->DrawShape();
        //........
    }
};

void Process() {
    //运行时装配
    // 运行时候组合
    MessagerImp* mImp = new PCMessagerImp();
    Messager* m = new MessagerLite(mImp);
}

要点总结

在这里插入图片描述

个人小结

和装饰模式非常像

但是这里的基类抽象方法很多(分类多,即不应该都集中在一个类中)

将基类按照功能再划分多个抽象类,再用指针指向其他的维度

装饰模式把成员多态指针放在一个中间的子类中,而桥模式则是在分离的基类中声明多态指针

然后该指针指向其他分离的基类

对象创建

在这里插入图片描述

工厂方法 Factory Method

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

在 MainForm 中需要分解对对象进行分解操作

可能是二进制文件,文本文件,图片文件等等

这时候我们需要在 MainForm 创建对应的对象,然后调用分解函数

代码1

非常直接的写一堆类,继承拥有virtual void split()的基类

FileSplitter1.cpp

非常直观的思路

class ISplitter {
public:
    virtual void split() = 0;
    virtual ~ISplitter() {
    }
};

class BinarySplitter : public ISplitter {

};

class TxtSplitter : public ISplitter {

};

class PictureSplitter : public ISplitter {

};

class VideoSplitter : public ISplitter {
    
};

MainForm1.cpp

几乎没有复用性

class MainForm : public Form {
public:
    void Button1_Click() {
        ISplitter* splitter = new BinarySplitter();  // 依赖具体类
        splitter->split();
    }
};
代码2

编写专门的工厂类

再编写具体的工厂子类

在具体操作的 MainForm 中通过构造把子类对象传入

用多态的方式,避免了 MainForm 中的固定

ISplitterFactory.cpp

// 抽象类
class ISplitter {
public:
    virtual void split() = 0;
    virtual ~ISplitter() {
    }
};

// 工厂基类
class SplitterFactory {
public:
    // 虚函数,延迟到运行时依赖
    virtual ISplitter* CreateSplitter() = 0;
    virtual ~SplitterFactory() {
    }
};

FileSplitter2.cpp

每个工厂类,对应一个具体类

// 具体类
class BinarySplitter : public ISplitter {

};

class TxtSplitter : public ISplitter {
    
};

class PictureSplitter : public ISplitter {

};

class VideoSplitter : public ISplitter {
    
};

// 具体工厂
class BinarySplitterFactory : public SplitterFactory {
public:
    virtual ISplitter* CreateSplitter() {
        return new BinarySplitter();
    }
};

class TxtSplitterFactory : public SplitterFactory {
public:
    virtual ISplitter* CreateSplitter() {
        return new TxtSplitter();
    }
};

class PictureSplitterFactory : public SplitterFactory {
public:
    virtual ISplitter* CreateSplitter() {
        return new PictureSplitter();
    }
};

class VideoSplitterFactory : public SplitterFactory {
public:
    virtual ISplitter* CreateSplitter() {
        return new VideoSplitter();
    }
};

MainForm2.cpp

把变化的内容从 MainFrom 中转移

使得 MainFrom 内容固定,通过接口或者构造传入变化的部分

// MainForm 没有具体类的依赖
// 只有抽象的类的依赖
// 把依赖具体类转移了
class MainForm : public Form {
    SplitterFactory* factory;  // 工厂

public:
    MainForm(SplitterFactory* factory) {
        this->factory = factory;
    }

    void Button1_Click() {
        ISplitter* splitter = factory->CreateSplitter();  // 多态new
        splitter->split();
    }
};

要点总结

在这里插入图片描述

个人小结

代码样例中的版本1是非常直观的写法,但是无法让 MainFrom 复用

而工厂方法让 MainFrom 通过接口独立,每个工厂类专门对应一个具体子类

虽然编写的类多了一个类别,但关系都是一一对应的,比较明确

抽象工厂 Abstract Factory

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

要对 SQL Server 数据库进行操作,需要有 Connection,Command,DataReader等一系列配套的操作

而当需要更换数据库的时候,也是这套操作

如何提高代码复用性

版本1

非常直接的写对应数据库的操作

class EmployeeDAO {
public:
    vector<EmployeeDO> GetEmployees() {
        SqlConnection* connection = new SqlConnection();
        connection->ConnectionString("...");

        SqlCommand* command = new SqlCommand();
        command->CommandText("...");
        command->SetConnection(connection);

        SqlDataReader* reader = command->ExecuteReader();
        while (reader->Read()) {
        }
    }
};
版本2

将每个操作都写成工厂方法的模式

但是有一个缺陷,就是这些操作其实是配套的一套

用户在不知情的情况下可能配套错,因此不能用传统的工厂方法

// 数据库访问有关的基类
class IDBConnection {};
class IDBConnectionFactory {
public:
    virtual IDBConnection* CreateDBConnection() = 0;
};

class IDBCommand {};
class IDBCommandFactory {
public:
    virtual IDBCommand* CreateDBCommand() = 0;
};

class IDataReader {};
class IDataReaderFactory {
public:
    virtual IDataReader* CreateDataReader() = 0;
};

// 支持SQL Server
class SqlConnection : public IDBConnection {};
class SqlConnectionFactory : public IDBConnectionFactory {};

class SqlCommand : public IDBCommand {};
class SqlCommandFactory : public IDBCommandFactory {};

class SqlDataReader : public IDataReader {};
class SqlDataReaderFactory : public IDataReaderFactory {};

// 支持Oracle
class OracleConnection : public IDBConnection {};
class OracleConnectionFactory : public IDBConnectionFactory {};

class OracleCommand : public IDBCommand {};
class OracleCommandFactory : public IDBCommandFactory {};

class OracleDataReader : public IDataReader {};
class OracleDataReaderFactory : public IDataReaderFactory {};

///
// 工厂方法 暴露的问题,这三个指针必须强制一套
class EmployeeDAO {
    IDBConnectionFactory* dbConnectionFactory;
    IDBCommandFactory* dbCommandFactory;
    IDataReaderFactory* dataReaderFactory;

public:
    vector<EmployeeDO> GetEmployees() {
        IDBConnection* connection = dbConnectionFactory->CreateDBConnection();
        connection->ConnectionString("...");

        IDBCommand* command = dbCommandFactory->CreateDBCommand();
        command->CommandText("...");
        command->SetConnection(connection);  // 关联性

        IDBDataReader* reader = command->ExecuteReader();  // 关联性
        while (reader->Read()) {
        }
    }
};
版本3

将三个配套的操作再封装成一个类

避免产生配套错误

// 数据库访问有关的基类
class IDBConnection {};

class IDBCommand {};

class IDataReader {};

// 三个操作,绑定到一起
// 此处是这个模式的稳定部分
class IDBFactory {
public:
    virtual IDBConnection* CreateDBConnection() = 0;
    virtual IDBCommand* CreateDBCommand() = 0;
    virtual IDataReader* CreateDataReader() = 0;
};

// 支持SQL Server
class SqlConnection : public IDBConnection {};
class SqlCommand : public IDBCommand {};
class SqlDataReader : public IDataReader {};

class SqlDBFactory : public IDBFactory {
public:
    virtual IDBConnection* CreateDBConnection() = 0;
    virtual IDBCommand* CreateDBCommand() = 0;
    virtual IDataReader* CreateDataReader() = 0;
};

// 支持Oracle
class OracleConnection : public IDBConnection {};
class OracleCommand : public IDBCommand {};
class OracleDataReader : public IDataReader {};

class OracleDBFactory : public IDBFactory {
public:
    virtual IDBConnection* CreateDBConnection() = 0;
    virtual IDBCommand* CreateDBCommand() = 0;
    virtual IDataReader* CreateDataReader() = 0;
};

class EmployeeDAO {
    // 保证是同一个工厂
    // 是一个 family
    IDBFactory* dbFactory;

public:
    vector<EmployeeDO> GetEmployees() {
        IDBConnection* connection = dbFactory->CreateDBConnection();
        connection->ConnectionString("...");

        IDBCommand* command = dbFactory->CreateDBCommand();
        command->CommandText("...");
        command->SetConnection(connection);  // 关联性

        IDBDataReader* reader = command->ExecuteReader();  // 关联性
        while (reader->Read()) {
        }
    }
};

要点总结

在这里插入图片描述

个人小结

抽象工厂和工厂方法非常相像

区别就在于抽象工厂需要一系列对应的对象,而工厂方法就是唯一对象

因此工厂方法也可以理解为抽象工厂的一个特例

原型模式 Prototype

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

情景沿用之前的文件分割器的代码

若这是一个复杂的对象,不能直接通过工厂模式从0创建

就可以使用原型模式直接克隆一个

版本1

Prototype.cpp

// 抽象类
class ISplitter {
public:
    virtual void split() = 0;
    virtual ISplitter* clone() = 0;  // 通过克隆自己来创建对象

    virtual ~ISplitter() {
    }
};

ConcretePrototype.cpp

通过拷贝构造函数,复制一个一样的对象再返回

其他语言没有拷贝构造,可以使用序列化等操作实现

// 具体类
class BinarySplitter : public ISplitter {
public:
    virtual ISplitter* clone() {
        // 通过 拷贝构造函数
        return new BinarySplitter(*this);
    }
};

class TxtSplitter : public ISplitter {
public:
    virtual ISplitter* clone() {
        return new TxtSplitter(*this);
    }
};

class PictureSplitter : public ISplitter {
public:
    virtual ISplitter* clone() {
        return new PictureSplitter(*this);
    }
};

class VideoSplitter : public ISplitter {
public:
    virtual ISplitter* clone() {
        return new VideoSplitter(*this);
    }
};

Client.cpp

原型对象不是用来使用的,是专门用来克隆的

class MainForm : public Form {
    ISplitter* prototype;  // 原型对象

public:
    MainForm(ISplitter* prototype) {
        this->prototype = prototype;
    }

    // 此处未考虑内存释放
    void Button1_Click() {
        // 原型对象不是用来使用的
        // 是专门用来克隆的
        ISplitter* splitter = prototype->clone();  // 克隆原型
        splitter->split();
    }
};

要点总结

在这里插入图片描述

个人小结

原型模式和工厂模式的区别主要在于

原型模式可以直接克隆一个比较复杂的,有过一定变化的状态

而工厂模式是从0开始创建对象

虽然老师在视频中说不常用,但我个人在项目中有遇到过场景的

构建器 Builder

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

在一个游戏中有一个造房子的操作

而这个操作的主要流程是固定的

但是不同房子的操作细节不同,如何更好的造房子

版本1

builder.cpp

class House {
    //....
};

class HouseBuilder {
public:
    House* GetResult() {
        return pHouse;
    }
    virtual ~HouseBuilder() {
    }

protected:
    House* pHouse;
    virtual void BuildPart1() = 0;
    virtual void BuildPart2() = 0;
    virtual void BuildPart3() = 0;
    virtual void BuildPart4() = 0;
    virtual void BuildPart5() = 0;
};

class StoneHouse : public House {};

class StoneHouseBuilder : public HouseBuilder {
protected:
    virtual void BuildPart1() {
        // pHouse->Part1 = ...;
    }
    virtual void BuildPart2() {
    }
    virtual void BuildPart3() {
    }
    virtual void BuildPart4() {
    }
    virtual void BuildPart5() {
    }
};

// 构造函数不能调用纯虚函数
// 是静态绑定 (C++基本功)
class HouseDirector {
public:
    HouseBuilder* pHouseBuilder;

    HouseDirector(HouseBuilder* pHouseBuilder) {
        this->pHouseBuilder = pHouseBuilder;
    }

    House* Construct() {
        pHouseBuilder->BuildPart1();

        for (int i = 0; i < 4; i++) {
            pHouseBuilder->BuildPart2();
        }

        bool flag = pHouseBuilder->BuildPart3();

        if (flag) {
            pHouseBuilder->BuildPart4();
        }

        pHouseBuilder->BuildPart5();

        return pHouseBuilder->GetResult();
    }
};

要点总结

在这里插入图片描述

个人小结

此模式和模板方法非常像,但不同的是在这个过程是含在这个对象的构造的概念中的

因此是属于创建类的设计模式

注意C++中不能在构造函数中用纯虚函数

对象性能

在这里插入图片描述

单件模式 Singleton

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

整体框架

class Singleton {
private:
    Singleton();
    Singleton(const Singleton& other);

public:
    static Singleton* getInstance();
    static Singleton* m_instance;
};

Singleton* Singleton::m_instance = nullptr;

// 线程非安全版本
Singleton* Singleton::getInstance() {
	// code
    return m_instance;
}

线程不安全版本

线程非安全版本

Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}

线程安全版本

线程安全版本,但锁的代价过高

Singleton* Singleton::getInstance() {
    Lock lock;
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}

双检查锁

双检查锁,但由于内存读写reorder不安全

Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {
        Lock lock;
        if (m_instance == nullptr) {
            m_instance = new Singleton();
        }
    }
    return m_instance;
}

C++11 标准库

C++ 11版本之后的跨平台实现

不是所有编译器都支持 volatile

std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);  // 获取内存fence
    if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);  // 释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

返回引用,一劳永逸

视频中未说的方法,在项目中非常常用

同时要将拷贝构造和=重载废弃

强制让外部使用引用接收

class Singleton {
    Singleton(Singleton& obj) = delete;
    Singleton& operator=(Singleton& obj) = delete;
};

Singleton& Singleton::getInstance() {
	static Singleton obj;
    return obj;
}

要点总结

在这里插入图片描述

个人小结

单件模式,现在通常称为单例模式

是在项目中非常常见的一种模式

也是我个人学习和使用的第一种设计模式

李老师的视频中讲的是返回指针的方式

但是还有一种返回对象的放置直接避免了指针的各种风险

享元模式 Flyweight

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

需要根据健值,获取对应的字体

要想办法避免大量的创建

版本1

flyweight.cpp

采用一个来进行共享

一般来说都规定为只读

class Font {
private:
    // unique object key
    string key;

    // object state
    //....

public:
    Font(const string& key) {
        //...
    }
};

class FontFactory {
private:
    map<string, Font*> fontPool;

public:
    Font* GetFont(const string& key) {
        map<string, Font*>::iterator item = fontPool.find(key);

        if (item != footPool.end()) {
            return fontPool[key];
        } else {
            Font* font = new Font(key);
            fontPool[key] = font;
            return font;
        }
    }

    void clear() {
        //...
    }
};

要点总结

在这里插入图片描述

个人小结

享元模式其实在一些语言的编译器内部有自己的实现

比如java的字符串常量池,可能实现方式不同,但是思想是一样的

接口隔离

在这里插入图片描述

门面模式 Facade

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

要点总结

在这里插入图片描述

个人小结

操作系统就是一种典型的门面模式

这是一种今典的设计思想,将递增的变化与外界隔离

仅提供外界稳定的接口,内部怎么改怎么写不暴露给外部,外部不关心

代理模式 Proxy

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

要在客户端获得一个对象

版本1

client.cpp

class ISubject {
public:
    virtual void process();
};

class RealSubject : public ISubject {
public:
    virtual void process() {
        //....
    }
};

class ClientApp {
    ISubject* subject;

public:
    ClientApp() {
        subject = new RealSubject();
    }

    void DoTask() {
        //...
        subject->process();

        //....
    }
};
版本2

proxy.cpp

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();
        
        //....
    }
};

要点总结

在这里插入图片描述

个人小结

本质就是增加一个中间层

让前后都觉得复杂的内容,到中间层操作

适配器 Adapter

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

有一个其他项目,或者老项目的代码想要在新代码中进行复用

为了适配新项目,要设计一个适配器

代码1

Adapter.cpp

// 目标接口(新接口)
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();
}


// 在stl中的应用
// 老接口转新接口
class stack {
    deqeue container;
};

class queue {
    deqeue container;
};

要点总结

在这里插入图片描述

个人小结

这个模式在新老项目,多人合作的时候非常重要 (即编写适配的过渡接口)

对象适配器和装饰模式的写法有点类似

也是继承一个接口,内部再声明一个多态指针

但实际作用还是有大区别的

中介者 Mediator

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

要点总结

在这里插入图片描述

个人小结

中介者可以不用显示的将各个对象的进行关联,而是通过一层中介隐藏这类关系

状态变化

在这里插入图片描述

状态模式 State

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

很多操作,具有很多状态判断的情形

在添加新状态后,如何减少原代码的修改

版本1

state1.cpp

暴力的if else状态判断

增加状态,就增加else分支

enum NetworkState {
    Network_Open,
    Network_Close,
    Network_Connect,
};

class NetworkProcessor {
    NetworkState state;

public:
    void Operation1() {
        if (state == Network_Open) {
            //**********
            state = Network_Close;
        } else if (state == Network_Close) {
            //..........
            state = Network_Connect;
        } else if (state == Network_Connect) {
            //$$$$$$$$$$
            state = Network_Open;
        }
    }

public:
    void Operation2() {
        if (state == Network_Open) {
            //**********
            state = Network_Connect;
        } else if (state == Network_Close) {
            //.....
            state = Network_Open;
        } else if (state == Network_Connect) {
            //$$$$$$$$$$
            state = Network_Close;
        }
    }

public:
    void Operation3() {
    }
};
版本2

state2.cpp

用一个多态指针,不断指向下一个状态

用链表的形式不断走向下一个状态

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;
        //...
    }
};

要点总结

在这里插入图片描述

个人小结

这个模式和策略模式非常像,也是会出现一堆if else

但是本模式更加接近有限状态机的模型

注意我们一般说的状态机都是有限的

备忘录 Memento

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

需要在一个状态经过一段改变后,又回到需要的状态点

版本1

memento.cpp

#include <string>
using namespace std;

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;

    // 其实就是暂存一个对象
    // 需要的时候再把对象set进去
    // 捕获对象状态,存储到备忘录
    Memento mem = orginator.createMomento();

    //... 改变orginator状态

    // 从备忘录中恢复
    orginator.setMomento(mem);
}

要点总结

在这里插入图片描述

个人小结

同名字一般,就是某个时间点或者状态下的状态

在需要回溯的时候再set回去

但是实际业务场景一般都非常复杂,不可能只有单个属性,因此一般就直接记录下一个对象

set的时候,再把整个对象传入,set函数再根据需要进行回溯拷贝等

数据结构

在这里插入图片描述

组合模式 Composite

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

如何在树形结构上,对整体与部分,用相同的操作进行操作

版本1

composite.cpp

#include <algorithm>
#include <iostream>
#include <list>
#include <string>

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 nodes
        for (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("leaf1");
    Leaf leaf2("leaf2");

    root.add(&treeNode1);
    treeNode1.add(&treeNode2);
    treeNode2.add(&leaf1);

    root.add(&treeNode3);
    treeNode3.add(&treeNode4);
    treeNode4.add(&leaf2);

        // 老师的源代码写错了 // process(root)
    // 访问该树
    Invoke(root);
}

要点总结

在这里插入图片描述

个人小结

这个写法,就是在树形结构上,套用装饰模式

继承一个抽象类,再在类内有抽象类的多态指针

树是一种特殊的图,可见本模式也可以处理一些图的结构,这就是考验开发人员对数据结构的基本功是否扎实了

迭代器 Iterator

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

用类的方式实现迭代器的效果

版本1

Iterator.cpp

#include <iostream>

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()) {
        std::cout << iter->current() << std::endl;
    }
}

要点总结

在这里插入图片描述

个人小结

作为C++的学习者,迭代器可以说是非常熟悉的一个东西了

现代C++拥有模板,泛式编程的方式,几乎不再使用类的方式实现迭代器了

但是像java,C#这类语言还是使用的类实现迭代器

其实方式不重要,目的是编写一种可以迭代的效果

个人在项目中还是使用类函数的方式编写迭代器

职责链 Chain of Resposibility

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

用链式的方式,不断递归搜索可以处理的子类

解决发送方和接收方有冗余的情况

版本1

ChainofResposibility.cpp

#include <iostream>
#include <string>

using namespace std;

// 请求类型
enum class RequestType {
    REQ_HANDLER1,
    REQ_HANDLER2,
    REQ_HANDLER3 
};

// 请求
class Request {
    string description;
    RequestType reqType;

public:
    Request(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 Request &req) {
        if (nextChain != nullptr)
            nextChain->handle(req);
    }

protected:
    virtual bool canHandleRequest(const Request &req) = 0;
    virtual void processRequest(const Request &req) = 0;

public:
    ChainHandler() {
        nextChain = nullptr;
    }
    void setNextChain(ChainHandler *next) {
        nextChain = next;
    }

    // 不断递归链表识别 req
    void handle(const Request &req) {
        // 子类的 canHandleRequest
        if (canHandleRequest(req)) {
            // 子类的 processRequest
            processRequest(req);
        } else {
            // 本类的 sendReqestToNextHandler
            sendReqestToNextHandler(req);
            /* 等价代码
            if (nextChain != nullptr)
                nextChain->handle(req);
            */
        }
    }
};

class Handler1 : public ChainHandler {
protected:
    bool canHandleRequest(const Request &req) override {
        return req.getReqType() == RequestType::REQ_HANDLER1;
    }
    void processRequest(const Request &req) override {
        cout << "Handler1 is handle Request: " << req.getDescription() << endl;
    }
};

class Handler2 : public ChainHandler {
protected:
    bool canHandleRequest(const Request &req) override {
        return req.getReqType() == RequestType::REQ_HANDLER2;
    }
    void processRequest(const Request &req) override {
        cout << "Handler2 is handle Request: " << req.getDescription() << endl;
    }
};

class Handler3 : public ChainHandler {
protected:
    bool canHandleRequest(const Request &req) override {
        return req.getReqType() == RequestType::REQ_HANDLER3;
    }
    void processRequest(const Request &req) override {
        cout << "Handler3 is handle Request: " << req.getDescription() << endl;
    }
};

int main() {
    // 构造链表
    Handler1 h1;
    Handler2 h2;
    Handler3 h3;
    h1.setNextChain(&h2);
    h2.setNextChain(&h3);

    Request req("process task ... ", RequestType::REQ_HANDLER3);
    // h1为链表头
    h1.handle(req);
    return 0;
}

要点总结

在这里插入图片描述

个人小结

其实处理这个请求接收方的情景也可以直接用一堆if else来处理 (似乎很多模式都可以来处理)

但是职责链的模式,更加明确了各自的职责

可以动态的绑定多个接收方,再进行处理

行为变化

在这里插入图片描述

命令模式 Command

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

良好的统一管理同一类的请求

版本1

Command.cpp

#include <iostream>
#include <string>
#include <vector>
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();
}

要点总结

在这里插入图片描述

个人小结

在请求方和接收方之间增加一层

这层将良好的管理双方的各种要求

虽然继承同一个抽象类,但是作用是不一样的,有的是特化,有的是统领

访问器 Vistor

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

在业务变更下,需要增加新方法,但是在基类中增加,则要在所有子类都增加,也有巨大的负担


这里老师把版本12搞反了

版本1

Visitor1.cpp

直接在基类增加方法,子类也要增加

#include <iostream>
using namespace std;

class Visitor;

class Element {
public:
    virtual void Func1() = 0;

    virtual void Func2(int data) = 0;
    virtual void Func3(int data) = 0;
    //...

    virtual ~Element() {
    }
};

class ElementA : public Element {
public:
    void Func1() override {
        //...
    }

    void Func2(int data) override {
        //...
    }
};

class ElementB : public Element {
public:
    void Func1() override {
        //***
    }

    void Func2(int data) override {
        //***
    }
};
版本2

Visitor2.cpp

双重多态的方式实现效果

#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 dispatch

    ElementA elementA;
    elementA.accept(visitor);

    return 0;
}

要点总结

在这里插入图片描述

个人小结

感觉这个模式有点鸡肋,当然也有这个场景要求的问题,过于苛刻了

需要确定扩展的子类,若要救确定了,或许很多地方就可以直接写死了

领域问题

在这里插入图片描述

解析器 Interpreter

动机

在这里插入图片描述

模式定义

在这里插入图片描述

结构

在这里插入图片描述

代码

情景

设计一个简单数学计算表达式的功能

在这里插入图片描述

版本1

main.cpp

#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;
}

要点总结

在这里插入图片描述

个人小结

算法题里做了好多这种题

一般来说都是处理简单或者有明确规则的字符串

复杂的就直接掉包吧

个人小结

23种设计模式每一种都包含了前人的智慧

在实际运用中不要死记,要思考什么是稳定的,什么是变化的,通过不断的重构获得更加优秀的代码

在稳定和变化中寻求一种平衡 是设计模式的核心宗旨

我们的目的不是消除变化,因为变化是不能消除的,只能通过转移变化来提高代码的复用性

每一种设计模式的表现形式必然不一样,即使是同一种模式,不同人写的也可能不一样,但是核心原理和目的是一样的

有时两份不同的代码可能表示的是同一种模式,而极为相似的代码可能表示的是截然不同的模式

有些模式其实极为相似,不同点可能是在实际业务场景或者人的思维角度的不同而导致的不同

我们虽然是软件行业的从业者,但是也可以从其他行业寻求智慧,要不断思考,不断重构,不断学习




743936)] (李建忠 C++) 23种设计模式.assets/image-20221214030212601.png)

结构

在这里插入图片描述

代码

情景

设计一个简单数学计算表达式的功能

在这里插入图片描述

版本1

main.cpp

#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;
}

要点总结

在这里插入图片描述

个人小结

算法题里做了好多这种题

一般来说都是处理简单或者有明确规则的字符串

复杂的就直接掉包吧

个人小结

23种设计模式每一种都包含了前人的智慧

在实际运用中不要死记,要思考什么是稳定的,什么是变化的,通过不断的重构获得更加优秀的代码

在稳定和变化中寻求一种平衡 是设计模式的核心宗旨

我们的目的不是消除变化,因为变化是不能消除的,只能通过转移变化来提高代码的复用性

每一种设计模式的表现形式必然不一样,即使是同一种模式,不同人写的也可能不一样,但是核心原理和目的是一样的

有时两份不同的代码可能表示的是同一种模式,而极为相似的代码可能表示的是截然不同的模式

有些模式其实极为相似,不同点可能是在实际业务场景或者人的思维角度的不同而导致的不同

我们虽然是软件行业的从业者,但是也可以从其他行业寻求智慧,要不断思考,不断重构,不断学习




END

  • 6
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: “设计模式”是一本著名的计算机科学书籍,作者是Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides。这本书介绍了23种常见的设计模式,这些设计模式是面向对象编程语言中可复用软件设计和架构的基础。其中包括创建型模式、结构型模式和行为型模式等,这些模式能够帮助开发者更好地组织代码结构,提高代码的可复用性和可维护性。 李建忠老师对《设计模式》一书进行了详细解读,并结合自己多年的软件开发经验,深入浅出地介绍了设计模式中每一种模式的使用场景、优点、缺点和使用方法。通过读完这本书和听取李建忠老师的解读,开发者可以深刻地理解并熟练运用这些设计模式,从而编写出更加高效、灵活、易于维护的代码。 如今,随着软件项目愈发复杂,开发者也越来越需要掌握和应用设计模式。而李建忠老师对《设计模式》一书的解读,无疑为那些想要系统学习设计模式的开发者提供了帮助和指引。此外,学习设计模式也是程序员晋升职位、提高技术水平的必经之路。因此,“设计模式”这本书及其相关的学习资料对于广大软件开发者而言都是非常重要的资料,值得认真学习和掌握。 ### 回答2: 设计模式是软件开发领域中的一个重要概念,它是指在软件设计过程中,经过长期实践和经验总结而形成的一套被广泛接受的最佳实践方法。 这些模式不是语法或代码的规则,而是一种解决特定问题的方案,描述了在特定情境下一种解决问题的最佳实践方法。 李建忠编写的《设计模式》是针对软件工程师和开发人员的一本重要著作,该书内容系统、详细,涵盖了23种常用设计模式,包括创建型模式、结构型模式和行为型模式等。通过学习这些模式,读者可以提高软件设计的质量和可重用性,同时也可以更好地理解和维护现有代码。 该书可以帮助读者了解面向对象设计的基本原则和模式,学习如何应用这些模式来解决实际的软件设计问题。作者李建忠深入浅出地讲解了每种模式的原理、应用场景以及实际案例,让读者更好地理解和应用每种模式。 总的来说,《设计模式》是一本非常实用的软件设计指南,为读者提供了一系列有用的工具和技能,可以让读者更加高效地开发出高质量的软件系统。该书对于软件工程师、软件开发人员和软件设计师都是一本必备的学习资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值