C++ 常见设计模式(8种)

1、单例模式

         确保一个类只有一个实例,并提供全局访问点。

        单例模式又分为饿汉式和懒汉式懒汉式。

        饿汉式 --- 线程安全但浪费空间 --- 在程序启动适合就创建好了单例实例

        懒汉式 --- 不浪费空间但线程不安全 --- 提供静态函数来实例化单例

        编码实现:

// 饿汉式
class SingletonEager{
private::
    SingletonEager(){}    //私有构造函数
    SingletonEager(const SingletonEager&) = delete; //禁止拷贝构造
    SingletonEager& operator=(const SingletonEager&) = delete;  //禁止赋值

public:
    static SingletonEager instanse; //静态实例
    static SingletonEager& getInstanse(){

        return instanse;
    }
     
    void doSomeThing(){
    }

}

SingletonEager SingletonEager::instanse;  //类外初始化,程序启动适合进行初始化

//只能直接通过静态函数调用

SingletonEager::getInstanse().doSomeThing();


// 懒汉式 (没考虑线程安全,可以使用线程锁避免多个线程同时调用静态函数)
class SingletonLazy {
private::
    SingletonLazy (){}    //私有构造函数
    SingletonLazy (const SingletonLazy &) = delete; //禁止拷贝构造
    SingletonLazy & operator=(const SingletonLazy &) = delete;  //禁止赋值

    // 静态指针,初始值为 nullptr
    static SingletonLazy* instance;


public:
    
    static SingletonLazy * getInstanse(){
        //局部静态实例,在第一次调用的时候初始化
         instanse = new SingletonLazy (); 
        return instanse;
    }
     
    void doSomeThing(){
    }

}

// 初始化静态成员指针为 nullptr
SingletonLazy* SingletonLazy::instance = nullptr;

//只能直接通过静态函数调用
SingletonLazy ::getInstanse()->doSomeThing();

2、普通工厂模式/静态工厂模式

实现步骤:定义产品基类与接口 --- 实现具体产品类 --- 定义工厂类(通常包含一个静态方法,使用多态,根据传入参数判断并创建相应的产品对象)

// 产品抽象类
class Product{
pubulic:
    virtual void use() = 0;  //产品使用方法
    virtual ~Product(){};
}

// 具体产品A
class ProductA{
pubulic:
    virtual void use() override{  //A产品使用方法
        cout << "use ProductA" << endk;
    }
}

// 具体产品B
class ProductB{
pubulic:
    virtual void use() override{  //A产品使用方法
        cout << "use ProductB" << endk;
    }
}


class SimpleFactory {
public:
    //常常包含一个静态方法
    static Product* createProduct(const string& type){
        if(type == 'A'){
            return new ProductA();
            
        }
        else if(ype == 'B'){
            return new ProductB();
        }
        else return nullptr;
    }

}


// 调用
Product* product =SimpleFactory::createProduct("A");  //记得delete product;

3、抽象工厂模式

抽象工厂模式相较于工厂模式是将工厂也抽象化,用于创建一系列相关或者相互依赖的产品族

// 产品接口
class Button {
public:
    virtual void paint() = 0;
    virtual ~Button() {}
};

class TextBox {
public:
    virtual void render() = 0;
    virtual ~TextBox() {}
};

// Windows 风格产品
class WindowsButton : public Button {
public:
    void paint() override { /* Windows 按钮绘制逻辑 */ }
};

class WindowsTextBox : public TextBox {
public:
    void render() override { /* Windows 文本框渲染逻辑 */ }
};

// Mac 风格产品
class MacButton : public Button {
public:
    void paint() override { /* Mac 按钮绘制逻辑 */ }
};

class MacTextBox : public TextBox {
public:
    void render() override { /* Mac 文本框渲染逻辑 */ }
};

// 抽象工厂接口
class GUIFactory {
public:
    virtual Button* createButton() = 0;
    virtual TextBox* createTextBox() = 0;
    virtual ~GUIFactory() {}
};

// Windows 工厂
class WindowsFactory : public GUIFactory {
public:
    Button* createButton() override {
        return new WindowsButton();
    }
    TextBox* createTextBox() override {
        return new WindowsTextBox();
    }
};

// Mac 工厂
class MacFactory : public GUIFactory {
public:
    Button* createButton() override {
        return new MacButton();
    }
    TextBox* createTextBox() override {
        return new MacTextBox();
    }
};


void createGUI(GUIFactory* factory) {
    Button* btn = factory->createButton();
    TextBox* txt = factory->createTextBox();
    btn->paint();
    txt->render();
    // 注意清理内存...
}



//再比如水果族

//抽象苹果
//中国苹果
//日本苹果
//美国苹果

//抽象香蕉
//中国香蕉
//日本香蕉
//美国香蕉

//抽象工厂
//中国工厂(含中国水果)
//日本工厂
//美国工厂

void createFruit(const 抽象工厂& factor){
    苹果* 苹果 = factor->create苹果;
    香蕉* 香蕉 = factor->create香蕉;
}

4、建造者模式

建造者模式允许你一步一步地构造一个复杂对象,而不必在一个巨大的构造函数中处理所有细节。

实现步骤:产品 --- 抽象建造者 --- 具体建造者 --- 指挥者

// 使用建造者模式构造一个简单的“房子”对象。房子由墙壁、屋顶和地基组成,
// 不同的具体建造者可以构造出不同风格的房子。
#include <iostream>
#include <string>
#include <memory>

// 产品类:房子
class House {
public:
    void setFoundation(const std::string &foundation) {
        foundation_ = foundation;
    }
    void setStructure(const std::string &structure) {
        structure_ = structure;
    }
    void setRoof(const std::string &roof) {
        roof_ = roof;
    }
    void show() const {
        std::cout << "House built with:\n"
                  << "Foundation: " << foundation_ << "\n"
                  << "Structure: " << structure_ << "\n"
                  << "Roof: " << roof_ << std::endl;
    }
private:
    std::string foundation_;
    std::string structure_;
    std::string roof_;
};

// 抽象建造者
class HouseBuilder {
public:
    virtual ~HouseBuilder() {}
    virtual void buildFoundation() = 0;
    virtual void buildStructure() = 0;
    virtual void buildRoof() = 0;
    virtual std::unique_ptr<House> getHouse() = 0;
};

// 具体建造者:建造现代风格的房子
class ModernHouseBuilder : public HouseBuilder {
public:
    ModernHouseBuilder() {
        house_ = std::make_unique<House>();
    }
    void buildFoundation() override {
        house_->setFoundation("Concrete foundation");
    }
    void buildStructure() override {
        house_->setStructure("Glass and steel structure");
    }
    void buildRoof() override {
        house_->setRoof("Flat roof");
    }
    std::unique_ptr<House> getHouse() override {
        return std::move(house_);
    }
private:
    std::unique_ptr<House> house_;
};

// 指挥者:负责按照步骤构造房子
class Director {
public:
    void setBuilder(HouseBuilder *builder) {
        builder_ = builder;
    }
    // 构造房子
    void constructHouse() {
        if(builder_) {
            builder_->buildFoundation();
            builder_->buildStructure();
            builder_->buildRoof();
        }
    }
private:
    HouseBuilder* builder_ = nullptr;
};

int main() {
    Director director;
    ModernHouseBuilder modernBuilder;

    director.setBuilder(&modernBuilder);
    director.constructHouse();
    std::unique_ptr<House> house = modernBuilder.getHouse();

    house->show();
    return 0;
}

5、适配器模式(对象适配器)

组成:目标接口(客户希望)  已有接口    适配器(将用户接口转换成已有接口的方法)

假设我们有一个已有类 LegacyPrinter,它提供了打印功能,但接口与客户端预期的 Printer 接口不一致,我们需要创建一个适配器将 LegacyPrinter 的接口转换为 Printer 接口。

#include <iostream>
#include <string>

// 目标接口,客户端期望使用该接口
class Printer {
public:
    virtual void print(const std::string &text) = 0;
    virtual ~Printer() {}
};

// 已有类,其接口与目标接口不匹配
class LegacyPrinter {
public:
    void oldPrint(const char* text) {
        std::cout << "LegacyPrinter prints: " << text << std::endl;
    }
};

// 对象适配器:包装 LegacyPrinter,实现目标接口 Printer
class PrinterAdapter : public Printer {
public:
    PrinterAdapter(LegacyPrinter* legacy) : legacyPrinter(legacy) {}

    void print(const std::string &text) override {
        // 将 std::string 转换为 const char*,调用已有方法
        legacyPrinter->oldPrint(text.c_str());
    }

private:
    LegacyPrinter* legacyPrinter;
};

int main() {
    // 创建已有对象
    LegacyPrinter legacyPrinter;
    // 用适配器包装已有对象
    Printer* printer = new PrinterAdapter(&legacyPrinter);

    // 客户端通过目标接口调用方法,不关心内部实现细节
    printer->print("Hello, Adapter Pattern!");

    delete printer;
    return 0;
}

6、装饰模式

结构型设计模式,用于动态地给对象添加功能,而不改变其接口。通过装饰模式,我们可以在运行时“包装”一个对象,并在包装过程中扩展或修改其行为,从而避免了通过继承来增加功能所带来的类爆炸问题。

组成:组件接口 --- 具体组件 --- 抽象装饰器 --- 具体装饰器

下面给出一个简单的 C++ 示例,展示如何使用装饰模式为一个文本打印器添加额外的装饰功能。

#include <iostream>
#include <string>

// 组件接口:定义打印的接口
class Printer {
public:
    virtual void print(const std::string &text) = 0;
    virtual ~Printer() {}
};

// 具体组件:基本的打印器
class BasicPrinter : public Printer {
public:
    void print(const std::string &text) override {
        std::cout << text;
    }
};

// 抽象装饰器:实现 Printer 接口,并持有一个 Printer 对象
class PrinterDecorator : public Printer {
protected:
    Printer* printer;
public:
    PrinterDecorator(Printer* p) : printer(p) {}
    virtual ~PrinterDecorator() {
        delete printer;
    }
    void print(const std::string &text) override {
        // 委托调用被装饰对象的方法
        printer->print(text);
    }
};

// 具体装饰器:为打印增加前后边框
class BorderPrinter : public PrinterDecorator {
public:
    BorderPrinter(Printer* p) : PrinterDecorator(p) {}
    void print(const std::string &text) override {
        std::cout << "-----------------" << std::endl;
        PrinterDecorator::print(text);
        std::cout << std::endl << "-----------------" << std::endl;
    }
};

// 另一个具体装饰器:为打印增加日志记录功能
class LoggingPrinter : public PrinterDecorator {
public:
    LoggingPrinter(Printer* p) : PrinterDecorator(p) {}
    void print(const std::string &text) override {
        std::cout << "[LOG] About to print text." << std::endl;
        PrinterDecorator::print(text);
        std::cout << std::endl << "[LOG] Finished printing." << std::endl;
    }
};

int main() {
    // 创建一个基本打印器
    Printer* basic = new BasicPrinter();

    // 用 BorderPrinter 装饰基本打印器,为文本添加边框
    Printer* bordered = new BorderPrinter(basic);

    // 再用 LoggingPrinter 装饰 bordered 打印器,增加日志记录
    Printer* decoratedPrinter = new LoggingPrinter(bordered);

    // 使用装饰后的打印器
    decoratedPrinter->print("Hello, Decorator Pattern!");

    delete decoratedPrinter;
    return 0;
}

7、策略模式

一种行为型设计模式,其核心思想是将一系列算法封装成独立的策略类,使它们可以互换使用,从而使得算法的变化独立于使用它的客户端。换句话说,策略模式允许在运行时选择不同的算法或行为,而无需修改使用这些算法的上下文对象。

组成:策略接口 --- 具体策略 --- 上下文

下面给出一个简单的 C++ 示例,展示如何使用策略模式来实现不同的排序算法。假设我们需要对一组数字排序,排序策略可以是冒泡排序、快速排序等,我们将这些排序算法分别封装为策略类。

#include <iostream>
#include <vector>
#include <algorithm>

// 策略接口,定义排序算法
class SortStrategy {
public:
    virtual void sort(std::vector<int>& data) = 0;
    virtual ~SortStrategy() {}
};

// 具体策略:冒泡排序
class BubbleSort : public SortStrategy {
public:
    void sort(std::vector<int>& data) override {
        int n = data.size();
        for (int i = 0; i < n - 1; ++i) {
            for (int j = 0; j < n - i - 1; ++j) {
                if (data[j] > data[j + 1]) {
                    std::swap(data[j], data[j + 1]);
                }
            }
        }
        std::cout << "Data sorted using BubbleSort." << std::endl;
    }
};

// 具体策略:标准库排序(快速排序的一个实现)
class QuickSort : public SortStrategy {
public:
    void sort(std::vector<int>& data) override {
        std::sort(data.begin(), data.end());
        std::cout << "Data sorted using QuickSort (std::sort)." << std::endl;
    }
};

// 上下文类,使用策略接口进行排序
class SortContext {
private:
    SortStrategy* strategy;
public:
    SortContext(SortStrategy* strat = nullptr) : strategy(strat) {}
    
    // 设置策略
    void setStrategy(SortStrategy* strat) {
        strategy = strat;
    }
    
    // 执行排序
    void executeSort(std::vector<int>& data) {
        if (strategy) {
            strategy->sort(data);
        } else {
            std::cout << "No sorting strategy set." << std::endl;
        }
    }
};

int main() {
    // 创建一个包含无序数据的数组
    std::vector<int> data = {5, 3, 8, 4, 2, 7, 1, 6};

    // 创建上下文对象
    SortContext context;

    // 使用冒泡排序策略
    BubbleSort bubble;
    context.setStrategy(&bubble);
    context.executeSort(data);
    
    // 输出排序结果
    std::cout << "Sorted Data: ";
    for (int num : data) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    // 打乱数据
    data = {5, 3, 8, 4, 2, 7, 1, 6};

    // 使用快速排序策略(std::sort实现)
    QuickSort quick;
    context.setStrategy(&quick);
    context.executeSort(data);
    
    // 输出排序结果
    std::cout << "Sorted Data: ";
    for (int num : data) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

8、模板方法模式

一种行为型设计模式,其核心思想是定义一个算法的骨架,将一些步骤的具体实现延迟到子类中。这样,父类中就能封装不变的部分,而将变化的部分留给子类实现,从而实现代码复用和算法灵活扩展。

组成:抽象类 --- 具体子类

假设我们有一个数据处理流程,需要从文件中读取数据、处理数据,再将处理结果写入文件。数据处理的整体流程固定,但具体的数据读取和处理方式可能因场景不同而有所变化。

#include <iostream>
#include <string>

// 抽象类,定义数据处理模板
class DataProcessor {
public:
    // 模板方法:定义算法骨架,不能被子类重写
    void process() {
        readData();
        processData();
        writeData();
    }

    virtual ~DataProcessor() {}

protected:
    // 抽象步骤:读取数据
    virtual void readData() = 0;
    // 抽象步骤:处理数据
    virtual void processData() = 0;
    // 抽象步骤:写入数据
    virtual void writeData() = 0;
};

// 具体子类:处理文本数据
class TextDataProcessor : public DataProcessor {
protected:
    void readData() override {
        std::cout << "Reading text data from file..." << std::endl;
        // 模拟读取数据操作
    }

    void processData() override {
        std::cout << "Processing text data..." << std::endl;
        // 模拟数据处理操作
    }

    void writeData() override {
        std::cout << "Writing processed text data to file..." << std::endl;
        // 模拟写入数据操作
    }
};

// 具体子类:处理二进制数据
class BinaryDataProcessor : public DataProcessor {
protected:
    void readData() override {
        std::cout << "Reading binary data from file..." << std::endl;
        // 模拟读取二进制数据操作
    }

    void processData() override {
        std::cout << "Processing binary data..." << std::endl;
        // 模拟二进制数据处理操作
    }

    void writeData() override {
        std::cout << "Writing processed binary data to file..." << std::endl;
        // 模拟写入二进制数据操作
    }
};

int main() {
    // 使用模板方法模式进行文本数据处理
    DataProcessor* processor1 = new TextDataProcessor();
    processor1->process(); // 调用模板方法,依次执行读、处理、写
    delete processor1;

    std::cout << "------------------------" << std::endl;

    // 使用模板方法模式进行二进制数据处理
    DataProcessor* processor2 = new BinaryDataProcessor();
    processor2->process();
    delete processor2;

    return 0;
}


​​​​​​除此之外我们也应该了解软件设计的七大原则,来帮助我们实现高质量的编程

软件设计七大原则(OOP原则)

  1. 单一职责原则:一个类只负责一项职责(比如一个类负责存储,另一个类负责显示)
  2. 开闭原则:软件实体(类、模块、函数)应该对扩展开放,对修改关闭。(使用抽象类定义接口行为,通过继承和多态实现扩展,而不是直接修改原有的类)
  3. 里氏替换原则:子类必须可以替换其父类,且不会影响程序的正确性(确保合理继承,比如父类是鸟,会飞的鸟,则企鹅不能继承会飞的鸟类)
  4. 接口隔离原则:客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上(将一个大接口拆分成多个小接口)
  5. 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖抽象;抽象不应该依赖细节,而是细节依赖抽象
  6. 迪米特法则:一个对象应该对其他对象有最少的了解,只与直接朋友通信(避免链式调用)
  7. 合成复用原则:尽量使用组合或者聚合has-a,而不是继承is-a(避免继承带来的强耦合)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值