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

news2024/11/25 14:30:54

文章目录

  • 前言
  • 组件协作
    • 模板方法 Template Method
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
        • 版本2
      • 变化原理
      • 要点总结
      • 个人小结
    • 策略模式 Strategy
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
        • 版本2
      • 要点总结
      • 个人小结
    • 观察者模式 Observer
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 场景
        • 版本1
        • 版本2
        • 版本2
      • 要点总结
      • 个人小结
  • 单一职责
    • 装饰模式 Decorator
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 代码1
        • 版本2
        • 版本3
      • 要点总结
      • 个人小结
    • 桥模式 Bridge
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 代码1
        • 代码2
      • 要点总结
      • 个人小结
  • 对象创建
    • 工厂方法 Factory Method
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 代码1
        • 代码2
      • 要点总结
      • 个人小结
    • 抽象工厂 Abstract Factory
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
        • 版本2
        • 版本3
      • 要点总结
      • 个人小结
    • 原型模式 Prototype
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
    • 构建器 Builder
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
  • 对象性能
    • 单件模式 Singleton
      • 动机
      • 模式定义
      • 结构
      • 代码
      • 要点总结
      • 个人小结
    • 享元模式 Flyweight
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
  • 接口隔离
    • 门面模式 Facade
      • 动机
      • 模式定义
      • 结构
      • 要点总结
      • 个人小结
    • 代理模式 Proxy
      • 动机
      • 模式定义
      • 结构
      • 代码
      • 情景
        • 版本1
        • 版本2
      • 要点总结
      • 个人小结
    • 适配器 Adapter
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 代码1
      • 要点总结
      • 个人小结
    • 中介者 Mediator
      • 动机
      • 模式定义
      • 结构
      • 要点总结
      • 个人小结
  • 状态变化
    • 状态模式 State
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
        • 版本2
      • 要点总结
      • 个人小结
    • 备忘录 Memento
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
  • 数据结构
    • 组合模式 Composite
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
    • 迭代器 Iterator
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
    • 职责链 Chain of Resposibility
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
  • 行为变化
    • 命令模式 Command
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
    • 访问器 Vistor
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
        • 版本2
      • 要点总结
      • 个人小结
  • 领域问题
    • 解析器 Interpreter
      • 动机
      • 模式定义
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
  • 个人小结
      • 结构
      • 代码
        • 情景
        • 版本1
      • 要点总结
      • 个人小结
  • 个人小结
  • END

前言

李建忠老师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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89217.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

斐波那契问题——上台阶问题

题目&#xff1a; 给定整数N&#xff0c;代表台阶数&#xff0c;一次可以跨2个或者1个台阶&#xff0c;返回有多少种走法。 举例&#xff1a; N3&#xff0c;可以三次跨一个台阶&#xff0c;也可以先跨2再跨1&#xff0c;也可以先跨1再跨2&#xff0c;共三种走法。 思路&…

上海还有哪些比较好的IB学校?

今天我们就一起来盘点沪上几所办学成绩比较好的IB学校&#xff0c;快来看看哪所学校才是孩子的最好选择&#xff01; Promise 上海民办平和学校 上海市民办平和学校&#xff08;Shanghai Pinghe School&#xff09;是由上海金桥&#xff08;集团&#xff09;有限公司于1996年9月…

常用Java接单平台一览

不少主攻Java的程序员兄弟除了工作&#xff0c;还会在空闲时间选择接单来增加自己的收入&#xff1b;对于那些生活在二三线的程序员兄弟们&#xff0c;通过接单&#xff0c;来获得与一线城市对等的收入。具体该怎么做&#xff0c;且听我娓娓道来。接下来干货满满&#xff0c;先…

前端—新增的嵌入多媒体元素与交互性元素

新增的嵌入多媒体元素与交互性元素 新增的嵌入多媒体元素有video和audio元素&#xff0c;分别是用来插入视频和声音的。值得注意的是&#xff0c;可以在开始标签和结束标签之间放置文本内容&#xff0c;这样旧版本的浏览器就可以显示出不支持该标签的信息。例如下面的代码。HT…

Qt Visual Studio添加Qt ui和编译注意事项

文章目录背景新建Widget 对象UIC程序生成ui_xxxx.h文件编译出错设置QtWidgetsTestClass.h的属性总结背景 工程中需要新的界面VS中新建Qt的ui文件&#xff0c;然后需要生成对应的.cpp 和 .h 文件 新建Widget 对象 生成对应的三个文件.ui, xxx. cpp, xxx.h 但是此时在QtWidget…

IDEA报错问题:If you already have a 64-bit JDK installed 解决方法【杭州多测师_王sir】【杭州多测师】...

启动IDEA的时候突然报错&#xff1a; 第一步&#xff1a;首先进入到C:\Users\用户名\.IdeaIC2019.3\config这个目录下面找到idea64.exe.vmoptions文件 第二步&#xff1a;通过notepad打开&#xff0c;进入编辑 第三步&#xff1a;然后修改配置如下&#xff1a;把Xms和Xmx的参数…

[MySQL]-主从同步实战

[MySQL]-主从同步实战 森格 | 2022年12月14日 本文主要为在平时work中遇到的主从同步上的问题的处理&#xff0c;对其进行巩固总结。 一、场景介绍 ​ 在一个风和日丽的下午4点半&#xff0c;突然就收到一个主从同步失败的提示&#xff0c;三两下打开从库一看&#xff0c;好嘛…

Stimulsoft Reports 2023.1.1 Crack 根据自身需求选择

Stimulsoft 专门从事 Microsoft .NET Framework 组件的开发。客户满意是公司的首要任务&#xff0c;因此它旨在生产顶级质量的软件。应用程序开发并不总是那么容易&#xff0c;但 Stimulsoft 尽最大努力帮助开发人员将当今市场上最先进的技术整合到他们的应用程序中 特征 Razor…

老照片修复怎么修?这三个方法可以让你实现修复操作

相信大家在家里翻看以前的照片时&#xff0c;会发现有部分照片颜色已经泛黄&#xff0c;内容也跟着变得有些模糊不清了&#xff0c;那当我们遇到这种情况的时候&#xff0c;应该怎么办呢&#xff1f;别担心&#xff0c;今天就来教大家几个实用的方法&#xff0c;让大家学会老照…

stm32 adc dma

ADC采集电压&#xff0c;使用DMA传输到内存 一&#xff0c;ADC设置 1,Mode。 这里我们使用ADC的独立模式。 2&#xff0c;时钟分频 ADCCLK由 PCLK2分频得到&#xff0c;最大时钟频率36M。ADC 时钟太快&#xff0c;采样可能不够准确&#xff0c;误差大。 3&#xff0c;采样分…

砥砺深耕,笃行致远向未来——中国社科院与美国杜兰金融管理硕士项目

想在一个领域里有所发展&#xff0c;相信深耕下去一定会有收获。工作中积累的实战经验或是在文章上看到的补给型知识大都属于碎片化的&#xff0c;系统的学习理论知识很重要&#xff0c;尤其对于金融行业&#xff0c;中国社科院与美国杜兰大学合办金融管理硕士项目的出现&#…

演讲实录|OpenMLDB 与阿里云 MaxCompute 生态集成

在 OpenMLDB 第 8 期 Meetup 中&#xff0c;OpenMLDB PMC 陈迪豪以出租车行车时间预测问题为例&#xff0c;使用 OpenMLDB 基于阿里云 MaxCompute 的 Serverless 服务搭建机器学习应用&#xff0c;从数据引入开始&#xff0c;实现了端到端的机器学习应用全流程构建。 云服务作…

软件工程---习题五

1.为每种类型的模块耦合举一个具体例子。   答&#xff1a;耦合式对一个软件结构内不同模块之间互联程度的度量。耦合强弱取决于接口的复杂度&#xff0c;进入或访问某一模块的点&#xff0c;以及通过接口的数据。一般模块之间的可能的连接方式有七种&#xff0c;构成耦合的七…

深入MVC模式和三层架构

MVC模式 MVC 是一种分层开发的模式&#xff0c;其中&#xff1a; M&#xff1a;Model&#xff0c;业务模型&#xff0c;处理业务 V&#xff1a;View&#xff0c;视图&#xff0c;界面展示 C&#xff1a;Controller&#xff0c;控制器&#xff0c;处理请求&#xff0c;调用模…

深度学习中的卷积操作

本文从信号处理中的互相关运算引入深度学习中的卷积。 然后介绍了不同的卷积类型&#xff0c;以及如何在pytorch中使用这些卷积层。 &#xff08;在看pytorch文档中的Conv1D/2D/3D的时候感到比较困惑&#xff0c;又很好奇深度学习中各种各样的卷积操作。于是结合整理几乎包含深…

Linux软硬链接与动静态库

&#x1f9f8;&#x1f9f8;&#x1f9f8;各位大佬大家好&#xff0c;我是猪皮兄弟&#x1f9f8;&#x1f9f8;&#x1f9f8; 文章目录一、创建软硬链接二、软硬链接①软链接②硬链接③硬链接的用处④软硬链接的区别三、库的作用①库与为什么用库②动静态库的加载过程四、动静…

大数据分析如何进行日志采集

最近经常和技术大牛在交流&#xff0c;每次的碰撞都会让我产生更多的想法。例如这次谈到的&#xff0c;某企业信息化用了二三十种的公有云服务、有二十多种业务系统的数据库使用了同一个物理库、云产品的稳定性是值得肯定的。今天我们就来谈一谈日志在数据库中的作用以及如何采…

[附源码]Python计算机毕业设计SSM基于java语言的在线电子书阅读系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于jsp+mysql+ssm小熊猫水果管理系统-计算机毕业设计

项目介绍 小熊猫水果管理系统是水果商业贸易中的一条非常重要的道路&#xff0c;可以把其从传统的实体模式中解放中来&#xff0c;网上购物可以为消费者提供巨大的便利。通过小熊猫水果管理系统这个平台&#xff0c;可以使用户足不出户就可以了解现今的流行趋势和丰富的水果信…

在中国程序员能不能干一辈子?

在中国程序员当然能干一辈子&#xff0c;因为35岁的程序员已经自动死亡&#xff0c;全网销声匿迹&#xff0c;查无此人了&#xff0c;这辈子已经玩完了&#xff08;雾 开个玩笑&#xff0c;就是看够了那些焦虑文学&#xff0c;我只想说&#xff1a; 程序员到35岁、45岁、55岁&…