设计模式(c++)

目录

开闭原则

开闭原则(Open/Closed Principle)指出软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭

合成复用原则

组合大于继承

设计模式

设计模式通常分为三种主要类型:创建型设计模式、结构型设计模式和行为型设计模式

  1. 常见的创建型设计模式包括:

    1. 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。

      用于控制资源的访问,如数据库连接或文件系统。

    2. 工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。常用于将对象创建的逻辑从应用逻辑中分离出来。

    3. 抽象工厂模式(Abstract Factory):提供一个接口,用于创建相关的或依赖对象的家族,而不需要明确指定具体类。适用于系统需要处理多系列产品,但希望客户端无需关心具体实现的情况。

    4. 建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。通常用于创建复杂对象,允许用户按步骤指定复杂对象的各个部分,在用户代码中隔离复杂对象的创建和组装过程。

    5. 原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

      适用于创建重复对象时,效率更高的场景。

  2. 常见的结构型设计模式包括:

    1. 适配器模式(Adapter):允许将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
    2. 装饰器模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。
    3. 代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。
    4. 桥接模式(Bridge):将抽象部分与实现部分分离,使它们都可以独立地变化。
    5. 外观模式(Facade):为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
    6. 组合模式(Composite):将对象组合成树形结构以表示"部分-整体"的层次结构,组合使得用户对单个对象和组合对象的使用具有一致性。
  3. 常见的行为型设计模式包括:

    1. 策略模式(Strategy):允许在运行时选择算法的行为。
    2. 观察者模式(Observer):当一个对象状态改变时,所有依赖于它的对象都会得到通知并被自动更新。
    3. 命令模式(Command):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化。
    4. 责任链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
    5. 状态模式(State):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
    6. 模板方法模式(Template Method):在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。

单例模式 (Singleton)

https://zhuanlan.zhihu.com/p/37469260

  • 保证一个类仅有一个实例,并提供一个访问它的全局访问点
  • 相比于全局变量,单例模式可以延迟初始化,在使用前不会被创建,全局变量通常在启动时立即初始化

用法示例:

  • 定义一个单例类 私有化它的构造函数
  • 使用类的私有静态指针变量指向类的唯一实例;
  • 使用一个公有的静态方法获取该实例。

懒汉版(Lazy Singleton)代码示例

cpp 复制代码
// 单例实例在第一次被使用时才进行初始化
class Singleton
{
private:
	static Singleton* instance;
private:
	Singleton() {};
	~Singleton() {};
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
public:
	static Singleton* getInstance() 
    {
		if(instance == NULL) 
			instance = new Singleton();
		return instance;
	}
};

// init static member
Singleton* Singleton::instance = NULL;

Meyers' Singleton 代码示例

cpp 复制代码
// C++0x之后该实现是线程安全的
class Singleton
{
private:
	Singleton() { };
	~Singleton() { };
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
public:
	static Singleton& getInstance() 
    {
		static Singleton instance;
		return instance;
	}
};

饿汉版(Eager Singleton)代码示例

cpp 复制代码
// 单例实例在程序运行时被立即执行初始化
class Singleton
{
private:
	static Singleton instance;
private:
	Singleton();
	~Singleton();
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
public:
	static Singleton& getInstance() {
		return instance;
	}
}

// initialize defaultly
Singleton Singleton::instance;

工厂方法模式(Factory Method)

  • 封装创建逻辑(解耦、可重用、一致性和可维护性)
  • 返回一个抽象类型的指针或接口,从而使得客户代码可以无需知道具体的产品类,只依赖于产品的接口或基类(多态)
  • 相比于简单工厂模式(一个工厂方法,根据接受的参数不同,而创建不同的产品实例),工厂方法模式符合开闭原则,即无需修改 原有的工厂类和方法,只需增加新的产品类和对应的工厂类,(灵活性、可扩展性)

用法示例

  • 定义抽象产品类Product
  • 定义A产品类ProductA 继承自Product
  • 定义B产品类ProductB 继承自Product
  • 定义抽象工厂类Creator
  • 定义A产品工厂类CreatorA
  • 定义B产品工厂类CreatorB
  • 创建X产品工厂类的实例 用于生成相应产品类的实例(均返回抽象类型Product的指针)

代码示例

cpp 复制代码
#include <iostream>
#include <memory>

class Product {
public:
    virtual void operation() = 0;
};

class ConcreteProductA : public Product {
public:
    void operation() override {
        std::cout << "Operation of ConcreteProductA" << std::endl;
    }
};

class ConcreteProductB : public Product {
public:
    void operation() override {
        std::cout << "Operation of ConcreteProductB" << std::endl;
    }
};

class Creator {
public:
    virtual Product* factoryMethod() = 0;
};

class ConcreteCreatorA : public Creator {
public:
    Product* factoryMethod() override {
        return new ConcreteProductA();
    }
};

class ConcreteCreatorB : public Creator {
public:
    Product* factoryFactyoryMethod() override {
        return new ConcreteProductB();
    }
};

// 使用示例
int main() {
    std::unique_ptr<Creator> creatorA(new ConcreteCreatorA());
    std::unique_ptr<Product> productA(creatorA->factoryMethod());
    productA->operation();
    
    std::unique_ptr<Creator> creatorB(new ConcreteCreatorB());
    std::unique_ptr<Product> productB(creatorB->factoryMethod());
    productB->operation();

	Creator* creater = new ConcreteCreatorX();	
	Product* product = creater->factoryMethod();	// 多态
	product->operation();							// 多态
    
    return 0;
}

适配器模式 (Adapter)

  • 将不兼容的对象包装起来使其能够与其他类协同工作
  • 将一个类的接口转换成另一个期望的接口
  • 分为类适配器和对象适配器
    • 类适配器使用继承(特别是多重继承)来实现适配:
      • 优点:可以重写部分被适配者(Adaptee)的行为。
      • 缺点:因为继承了被适配者,所以耦合度较高。
    • 对象适配器使用组合来实现适配
      • 优点:更加灵活,减少了类之间的耦合度。
      • 缺点:需要额外的指针来访问被适配者。

用法示例

  • 类适配器:公有继承新接口(虚基类),私有继承旧接口,重写新接口中的方法(调用旧接口方法)
  • 对象适配器:公有继承新接口,拥有一个旧接口对象的引用,重写新接口中的方法(调用旧接口对象的成员方法)

代码示例

cpp 复制代码
class OldRectangle {
public:
    void draw() {
        std::cout << "Old Rectangle draw method." << std::endl;
    }
};

class NewShape {
public:
    virtual void display() = 0;
    virtual ~NewShape() {}
};

// 类适配器
class RectangleAdapter : public NewShape, private OldRectangle {
public:
    void display() override {
        // 调用 OldRectangle 的 draw 方法
        draw();
    }
};

// 对象适配器
class RectangleAdapter : public NewShape {
private:
    OldRectangle* oldRectangle;  // 持有一个对旧接口类的引用

public:
    RectangleAdapter(OldRectangle* rectangle) : oldRectangle(rectangle) {}

    void display() override {
        // 调用 OldRectangle 的 draw 方法
        oldRectangle->draw();
    }

    ~RectangleAdapter() {
        delete oldRectangle;
    }
};

int main() {
    OldRectangle* oldRectangle = new OldRectangle();
    NewShape* rectangleAdapter = new RectangleAdapter(oldRectangle);
    rectangleAdapter->display();

    delete rectangleAdapter;  // 注意:这将正确地释放适配器和旧的矩形对象
    return 0;
}

外观模式 (Facade)

  • 提供了一个统一的接口来访问子系统中的一组接口,来简化系统接口,减少用户对子系统的耦合
  • 不符合开闭原则

用法示例

  • 定义一个外观类,拥有其他类对象作为成员,其函数成员的实现为:一组对其他对象成员函数的调用

代码示例

cpp 复制代码
class MultimediaSystemFacade {
private:
    AudioPlayer audioPlayer;
    VideoPlayer videoPlayer;
    Broadcaster broadcaster;

public:
    void playMultimedia() {
        audioPlayer.playAudio();
        videoPlayer.playVideo();
    }

    void sendMessage(const std::string& message) {
        broadcaster.broadcastMessage(message);
    }
};

代理模式(Proxy)

  • 提供一个代替对象(代理对象)来控制对原对象的访问。通常用于延迟处理操作、控制访问、提供智能指引等功能
  • 广泛应用于网络服务、内存中的大对象管理、安全控制等多种场景中
  • 主要有三种类型:
    • 虚拟代理:在需要时才创建开销很大的对象。
    • 保护代理:控制对原始对象的访问,用于对象应有不同的访问权限时。
    • 智能引用代理:当调用对象的特定方法时,执行一些附加操作,如计数对象引用次数、记录日志等。

用法示例

  • 定义一个代理类,拥有被代理类对象作为成员,通过代理类调用被代理类的方法,从而在调用前添加额外的控制,实现延迟处理操作、控制访问、提供智能指引等

桥接模式(Bridge)

  • 提供一个桥接结构,将抽象层和实现层解耦,从而简化了复杂系统的类结构
  • 一维扩展用继承/组合,多维扩展(品牌与产品)用桥接

用法示例

  • Abstraction (抽象类):定义抽象类的接口,它包含一个指向 Implementor 类型对象的引用。
  • RefinedAbstraction (扩充抽象类):扩展 Abstraction 定义的接口。
  • Implementor (实现类接口):定义实现类的接口,这些类具体实现基本操作,但在 Abstraction 中以抽象方式提供。
  • ConcreteImplementor (具体实现类):Implementor 接口的具体实现。

例如:用于不同类型设备(如电视、无线投影仪等)的遥控器应用,每种设备都有不同的实现,但控制接口相似(开/关、调节音量等)

  • 抽象 (Abstraction)
    抽象部分由 RemoteControl 类及其扩展类 AdvancedRemoteControl 表示。这个抽象层定义了高层和通用的操作逻辑,如切换电源 (togglePower)、调节音量 (volumeUp, volumeDown) 以及静音操作 (mute)。
    • RemoteControl (基本遥控): 提供了基础的遥控功能。
    • AdvancedRemoteControl (高级遥控): 在基本遥控的基础上增加了额外的功能,例如静音。
  • 实现 (Implementor)
    实现部分由 Device 接口及其具体实现类 TV 和 Radio 表示。这个层面处理关于设备的具体操作细节,如设备的开关状态和音量控制具体是如何执行的。
    • Device (设备接口): 定义了操作设备必须实现的方法,例如 enable(), disable(), getVolume(),
      setVolume()。
    • TV (电视): 实现了Device接口,提供了电视特有的操作方式。
    • Radio (收音机): 同样实现了Device接口,按照收音机的方式执行操作。
  • 桥接连接
    RemoteControl (抽象) 持有一个到 Device (实现) 的引用。这个引用允许抽象层通过实现层提供的接口与具体的设备进行交互,而不需要关心这些设备的具体实现细节。这就是所谓的"桥接",即抽象层和实现层之间通过组合而非继承来建立联系,从而使得两者可以独立地变化。

装饰器模式(包装模式)(Decorator)

  • 动态地给一个对象添加一些额外的职责或功能,也是继承关系的一种代替方案(合成复用原则:组合大于继承),它通过组合而非继承增加功能,从而避免了由于继承引入的静态特性

用法示例

  • 定义装饰器类,维持一个指向被装饰类对象的引用,实现被装饰类的接口,另外添加新的功能

模版方法模式(Template Method)

  • 定义了一个操作中的算法的框架,将一些步骤的实现延迟到子类,允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤
  • 封装不变部分,扩展可变部分;提取公共代码,便于维护;行为由父类控制,子类实现

用法示例

  1. 定义抽象类,设置算法框架,实现固定部分,声明由子类实现的抽象操作。
  2. 定义继承自抽象类的具体类,实现抽象类中定义的抽象方法和钩子(hook)

代码示例

cpp 复制代码
// 抽象类
class Beverage {
public:
    // 模板方法,定义算法框架
    void prepareRecipe() {
        boilWater();
        brew();           // 特定于子类的步骤
        pourInCup();
        addCondiments();  // 特定于子类的步骤
    }
protected:
    virtual void brew() = 0;         // 由子类实现
    virtual void addCondiments() = 0;// 由子类实现
    void boilWater() {std::cout << "Boiling water" << std::endl;}
    void pourInCup() {std::cout << "Pouring into cup" << std::endl;}
    virtual ~Beverage() {}
};

// 具体类:咖啡
class Coffee : public Beverage {
protected:
    void brew() override {std::cout << "Dripping Coffee through filter" << std::endl;}
    void addCondiments() override {std::cout << "Adding Sugar and Milk" << std::endl;}
};

// 具体类:茶
class Tea : public Beverage {
protected:
    void brew() override {std::cout << "Steeping the tea" << std::endl;}
    void addCondiments() override {std::cout << "Adding Lemon" << std::endl;}
};

策略模式(Strategy)

  • 定义了一组算法,他们可以以相同的接口共享。在根据不同的条件选择不同的行为时,可以使用此模式进行解耦,使代码更易于维护和扩展
  • 使算法独立于使用他们的客户而独立变化。(在有多种算法相似的情况下,避免使用if...else所带来的复杂和难以维护)。

用法示例

  1. 定义抽象策略类,提供公共接口
  2. 定义继承自抽象策略类的具体策略类,提供具体算法实现
  3. 定义上下文类,其中维护一个对策略类对象的引用,用于设定策略,执行策略

观察者模式(Observer)

  • 定义了对象之间的一对多依赖关系,当一个对象改变状态时,所有依赖于它的对象都会得到通知并自动更新
  • 常用于实现分布式事件处理系统、在应用中实现跨系统的消息交换等场景

用法示例

  • Subject(主题):也称为"被观察者",维护一份观察者列表,并提供用于增加或删除观察者的方法。
  • Observer(观察者):为那些在主题状态发生改变时需获得通知的对象定义一个更新接口。
  • ConcreteSubject(具体主题):状态变化时,向 Observer 发送通知,存储 Observer 对象关心的数据。
  • ConcreteObserver(具体观察者):实现 Observer 的更新接口以使自身状态与主题的状态相协调。

代码示例

cpp 复制代码
// Observer interface
class Observer {
public:
    virtual void update(float price) = 0;
    virtual ~Observer() {}
};

// Subject interface
class Subject {
public:
    virtual void registerObserver(Observer* observer) = 0;
    virtual void removeObserver(Observer* observer) = 0;
    virtual void notifyObservers() = 0;
    virtual ~Subject() {}
};

class Stock : public Subject {
private:
    std::list<Observer*> observers;
    float price;
public:
    // Register an observer
    void registerObserver(Observer* observer) override {observers.push_back(observer);}
    // Remove an observer
    void removeObserver(Observer* observer) override {observers.remove(observer);}
    // Notify all registered observers
    void notifyObservers() override {
        for (auto& observer : observers) {
            observer->update(price);
        }
    }
    // Method to update price
    void setPrice(float newPrice) {price = newPrice; notifyObservers();}
};

class PriceDisplay : public Observer {
private:
    float price;
public:
    void update(float newPrice) override {price = newPrice; display();}
    void display() const {std::cout << "Current Stock Price: $" << price << std::endl;}
};

int main() {
    Stock stock;                           // Create a Stock object which is the subject.
    PriceDisplay display;                  // Create an observer.

    stock.registerObserver(&display);      // Register the observer with the subject.
    
    stock.setPrice(150.0f);                // Change the stock price, triggers notifications.
    stock.setPrice(155.5f);                // Change the stock price again.

    stock.removeObserver(&display);        // Unregister the observer.
    stock.setPrice(160.0f);                // No notification this time.

    return 0;
}

责任链模式(Chain of Responsibility)

  • 将请求的发送者和接收者解耦
  • 多个对象都有机会处理请求,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

用法示例

  1. 定义处理者基类,拥有一个基类对象的引用(下一个处理者),提供处理请求的接口,提供设置下一个处理者的接口。
  2. 定义派生自基类的具体处理者类,实现处理请求的接口:处理它所负责的请求,如果可以处理就处理,不能处理就将请求转发给链上的下一个对象。

代码示例

cpp 复制代码
#include <iostream>
#include <string>

// Handler
class Approver {
protected:
    Approver* successor = nullptr;
public:
    void setSuccessor(Approver* successor) {this->successor = successor;}
    virtual void processRequest(int amount) = 0;
    virtual ~Approver() {}
};

// ConcreteHandler1
class Manager : public Approver {
public:
    void processRequest(int amount) override {
        if (amount < 1000) {
            std::cout << "Manager will approve $" << amount << std::endl;
        } else if (successor) {
            successor->processRequest(amount);
        }
    }
};

// ConcreteHandler2
class Director : public Approver {
public:
    void processRequest(int amount) override {
        if (amount < 5000) {
            std::cout << "Director will approve $" << amount << std::endl;
        } else if (successor) {
            successor->processRequest(amount);
        }
    }
};

// ConcreteHandler3
class CEO : public Approver {
public:
    void processRequest(int amount) override {
        if (amount < 20000) {
            std::cout << "CEO will approve $" << amount << std::endl;
        } else {
            std::cout << "Request $" << amount << " needs further meeting!" << std::endl;
        }
    }
};

int main() {
    Manager manager;
    Director director;
    CEO ceo;

    // Set up the chain
    manager.setSuccessor(&director);
    director.setSuccessor(&ceo);

    // Various requests
    manager.processRequest(500);   // Manager handles this request
    manager.processRequest(1500);  // Director handles this request
    manager.processRequest(7000);  // CEO handles this request
    manager.processRequest(50000); // Needs further meeting
    
    return 0;
}
相关推荐
菠菜很好吃2 分钟前
Java增加线程后kafka仍然消费很慢
java·开发语言·kafka
s_nshine22 分钟前
将 build.gradle 配置从 Groovy 迁移到 Kotlin
android·开发语言·kotlin·gradle·groovy·build
Jerry_正弦22 分钟前
Kotlin中object关键字的作用
android·开发语言·kotlin
wjs202426 分钟前
R 绘图 - 饼图
开发语言
buyue__26 分钟前
Kotlin/Android中执行HTTP请求
android·开发语言·kotlin
Jerry_正弦26 分钟前
Kotlin模仿Rxjava进行数据的流式转换实现
开发语言·kotlin·rxjava
向宇it33 分钟前
【unity实战】使用Unity实现动作游戏的攻击 连击 轻重攻击和打击感
开发语言·游戏·unity·游戏引擎
ZJ_.1 小时前
Node.js 使用 gRPC:从定义到实现
java·开发语言·javascript·分布式·rpc·架构·node.js
阿龍17871 小时前
Qt中udp指令,大小端,帧头帧尾实际示例
网络·c++·qt·网络协议·udp