常用设计模式

一、什么是设计模式

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的代码设计经验总结,旨在解决面向对象设计中反复出现的问题,提升代码的可重用性、可理解性和可靠性。以下从多个维度详细讲解:

一、设计模式的核心价值

  • 代码复用:避免重复造轮子,直接应用经过验证的解决方案。
  • 易维护性:遵循设计原则,降低代码耦合,使系统更灵活,修改或扩展时影响范围小。
  • 沟通便利:提供公共术语,便于开发者交流设计思路(如提到 "单例模式",大家都知道是确保一个类仅有一个实例)。

二、起源与经典著作

1994 年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著《Design Patterns: Elements of Reusable Object-Oriented Software》,首次系统提出 23 种设计模式,四位作者被称为 "四人帮(GOF)",该书成为设计模式领域的奠基之作。

三、设计模式的分类

根据目的和行为方式,分为三大类:

  1. 创建型模式
    • 目标:灵活创建对象,隐藏创建逻辑。
    • 包括:工厂模式、抽象工厂模式、单例模式(确保类只有一个实例)、建造者模式(复杂对象分步构建)、原型模式(通过拷贝原型创建新对象)。
  2. 结构型模式
    • 目标:关注类与对象的组合,优化结构。
    • 包括:适配器模式(转换接口使不兼容类协作)、代理模式(为对象提供代理控制访问)、桥接模式(分离抽象与实现)、装饰器模式(动态添加对象职责)等。
  3. 行为型模式
    • 目标:处理对象间的交互与行为分配。
    • 包括:观察者模式(对象状态变化时通知依赖者)、策略模式(封装算法并可互换)、命令模式(将请求封装为对象)、责任链模式(请求沿链传递直至处理)等。

二、设计原则

1.依赖倒置

高层模块不应该依赖低层模块,两者都应该依赖抽象;
抽象不应该依赖具体实现,具体实现应该依赖于抽象;

自动驾驶系统公司是高层,汽车生产厂商为低层,它们不应该互 相依赖,一方变动另一方也会跟着变动;而应该抽象一个自动驾 驶行业标准,高层和低层都依赖它;这样以来就解耦了两方的变 动;自动驾驶系统、汽车生产厂商都是具体实现,它们应该都依
赖自动驾驶行业标准(抽象);

2.开放封闭

一个类应该对扩展(组合和继承)开放,对修改关闭;

3.面向接口

不将变量类型声明为某个特定的具体类,而是声明为某个接口;
客户程序无需获知对象的具体类型,只需要知道对象所具有的接口;
减少系统中各部分的依赖关系,从而实现" 高内聚、松耦合 " 的类 型设计方案;

4.封装变化点

将稳定点和变化点分离,扩展修改变化点;让稳定点和变化点的 实现层次分离;

5.单一职责

一个类应该仅有一个引起它变化的原因;

6.里氏替换

子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责;

7.接口隔离

不应该强迫客户依赖于它们不用的方法;
一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责;
客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应 该建立在最小的接口上。

8.组合优于继承

继承耦合度高,组合耦合度低。

三、常用设计模式

1.模版方法

模板模式是一种行为设计模式,它定义了一个算法的框架,将算法的具体步骤延迟到子类中实现。模板模式通过定义一个算法的骨架,将算法中不变的部分放在父类中实现,而将可变的部分延迟到子类中实现,从而实现代码复用和减少重复。

模板模式解决了以下问题:

  1. **代码复用**:模板模式通过将算法的通用部分放在父类中实现,实现了代码的复用。子类只需要实现特定的步骤,从而避免了重复编写相似的代码。

  2. **提高扩展性**:模板模式通过定义算法的框架,使得子类可以灵活地扩展或修改算法中的特定步骤,而不影响算法的整体结构。

  3. **实现开闭原则**:模板模式符合开闭原则,通过在父类中定义算法的骨架,可以在不修改父类的情况下扩展或修改子类的行为。

  4. **约束子类行为**:模板模式限制了子类可以实现的算法的范围,确保了算法的执行顺序和逻辑。

总的来说,模板模式适用于当算法中存在一组共同的步骤,但其中某些步骤的具体实现可能会有所不同的情况。通过模板模式,可以将通用的算法步骤放在父类中实现,而将可变的部分延迟到子类中实现,从而提高代码的复用性和灵活性。

cpp 复制代码
#include <iostream>

// AbstractClass 是抽象类,定义了模板方法和抽象方法
class AbstractClass {
public:
    // 模板方法,定义了算法的骨架
    void templateMethod() {
        step1();
        step2();
        step3();
    }

    // 抽象方法,需要子类实现
    virtual void step1() = 0;
    virtual void step2() = 0;
    virtual void step3() = 0;
};

// ConcreteClass 是具体子类,实现了抽象方法
class ConcreteClass : public AbstractClass {
public:
    void step1() override {
        std::cout << "Step 1" << std::endl;
    }

    void step2() override {
        std::cout << "Step 2" << std::endl;
    }

    void step3() override {
        std::cout << "Step 3" << std::endl;
    }
};

int main() {
    AbstractClass* myObject = new ConcreteClass();
    myObject->templateMethod();

    delete myObject;

    return 0;
}

2.观察者模式

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通 知并自动更新。

观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听并收到目标对象状态的变化。在这种模式中,当目标对象的状态发生变化时,所有依赖它的观察者对象都会得到通知并自动更新。

观察者模式解决了以下问题:

  1. **解耦观察者和目标**:观察者模式使得目标对象和观察者对象之间的耦合度降低,目标对象无需知道观察者的具体实现,只需要维护一个观察者列表进行状态通知即可。

  2. **支持广播通知**:目标对象状态的变化可以同时通知多个观察者对象,使得系统中的不同部分能够及时响应变化。

  3. **实现开闭原则**:通过观察者模式,可以在不修改目标对象和观察者对象的情况下,动态地添加新的观察者或删除现有的观察者,符合开闭原则。

  4. **降低系统的复杂性**:观察者模式将目标对象和观察者对象分离,使得系统中各个部分之间的交互变得简单,提高了系统的可维护性和扩展性。

总的来说,观察者模式适用于当一个对象的状态发生变化需要通知其他对象,并且这些对象需要根据状态的变化做出相应的处理时。这种模式在实现事件处理、GUI开发、消息通知等场景中被广泛应用。

cpp 复制代码
//是把客户端加入到对应的主题里,主题提供接口,当主题的状态变化,就通知所有加入的客户端

#include <iostream>
#include <vector>

// 观察者类 Observer
class Observer {
public:
    virtual void update() = 0;
};
/
// 主题类 Subject
class Subject {
public:
    // 添加观察者
    void attach(Observer* observer) {
        observers.push_back(observer);
    }
    // 移除观察者
    void detach(Observer* observer) {
        for (auto it = observers.begin(); it != observers.end(); ++it) {
            if (*it == observer) {
                observers.erase(it);
                break;
            }
        }
    }

    // 通知观察者
    void notify() {
        for (Observer* observer : observers) {  //observers容器里的每一个元素赋值给observer,并调用update()  
            observer->update();
        }
    }
    // 设置状态
    void setState(int state) {
        this->state = state;
        notify();
    }
    int getState() {
        return state;
    }
private:
    std::vector<Observer*> observers;
    int state;
};
///
// 具体观察者类 ConcreteObserver
class ConcreteObserver : public Observer {
public:
    ConcreteObserver(Subject* subject) : subject(subject) {   
        //上面的成员初始化列表等价于:
        //this->subject=subject;
        subject->attach(this);    //这里的this是当前对象,observer1或者observer2,也就是说Subject类创建的subject对象中的observers容器尾插新元素
    }

    ~ConcreteObserver() {
        subject->detach(this);
    }

    void update() override {
        //这块的代码逻辑是:当主函数调用setState函数的时候,将subject类中的state赋值,并通知观察者
        //被通知后,调用这个代码,打印state的值
        std::cout << "Observer received update. New state: " << subject->getState() << std::endl;   
    }

private:
    Subject* subject;
};

int main() {
    Subject subject;
    ConcreteObserver observer1(&subject);
    ConcreteObserver observer2(&subject);

    subject.setState(1);

    return 0;
}

3.策略模式

定义一系列算法,把它们一个个封装起来,并且使它们可互相 替换。该模式使得算法可独立于使用它的客户程序而变化。

策略模式是一种行为设计模式,它定义了一系列算法,并将每种算法封装成单独的策略类,使得这些算法可以相互替换,而且客户端代码不受影响。在策略模式中,算法的选择可以在运行时动态改变,从而实现算法的灵活性和可扩展性。

策略模式的目的是将算法的定义、使用和管理分离,以便能够独立地变更和切换不同的算法策略,而不需要修改客户端代码。通过策略模式,我们可以将算法的实现细节封装在独立的策略类中,从而提高代码的可维护性、可读性和灵活性。

策略模式通常用于以下情况:

  1. 当一个类的行为取决于多种算法中的一种,且这些算法可以独立于该类变化时。

  2. 当需要在运行时动态地选择算法时,即使在相同接口下也可以选择不同的算法。

  3. 当一个类有多个行为变体,可以使用策略模式将这些变体实现为不同的策略类。

总的来说,策略模式的目的是使算法的选择和使用与算法的实现相分离,从而提高代码的灵活性、可扩展性和可维护性。

cpp 复制代码
//客户端使用统一的上下文接口,而不同的策略使用统一的接口供上下文类调用

#include <iostream>

// 策略接口
class TravelStrategy {
public:
    virtual void travel() = 0;
    virtual ~TravelStrategy() {}
};

// 具体策略:公交出行策略
class BusTravelStrategy : public TravelStrategy {
public:
    void travel() override {
        std::cout << "乘坐公交出行" << std::endl;
    }
};

// 具体策略:地铁出行策略
class SubwayTravelStrategy : public TravelStrategy {
public:
    void travel() override {
        std::cout << "乘坐地铁出行" << std::endl;
    }
};

// 具体策略:打车出行策略
class TaxiTravelStrategy : public TravelStrategy {
public:
    void travel() override {
        std::cout << "打车出行" << std::endl;
    }
};

// 上下文:出行规划器
class TravelPlanner {
private:
    TravelStrategy* strategy;
public:
    TravelPlanner(TravelStrategy* strategy) : strategy(strategy) {}

    void setStrategy(TravelStrategy* strategy) {
        delete this->strategy;
        this->strategy = strategy;
    }

    void planTravel() {
        strategy->travel();
    }

    ~TravelPlanner() {
        delete strategy;
    }
};

int main() {
    TravelPlanner planner(new BusTravelStrategy());
    planner.planTravel();

    planner.setStrategy(new SubwayTravelStrategy());
    planner.planTravel();

    planner.setStrategy(new TaxiTravelStrategy());
    planner.planTravel();

    return 0;
}    

4、单例模式

保证一个类仅有一个实例,并提供一个该实例的全局访问点。

(1)版本一

cpp 复制代码
//单例模式仅有一个实例,并提供一个全局访问点
#include<iostream>
using namespace std;

class singleton{
public:
    static singleton *GetInstance(){  //首次创建实例,会一直持续到函数结束调用析构函数释放
        if(_instance==NULL){
            _instance=new singleton();
        }
        return _instance;
    }
private:
    singleton(){};
    ~singleton(){};
    //单例模式,就是一个类中只允许初始化一个实例,那就要禁用其他
    singleton(const singleton&)=delete;
    singleton& operator=(const singleton&)=delete;
    singleton(singleton&&)=delete;
    singleton& operator=(singleton&&)=delete;
    static singleton *_instance;    //静态全局变量,所有类的对象可以共享访问这个资源
};
singleton *singleton::_instance=NULL;

#if 0
//如果不禁用:
Singleton* s1 = Singleton::getInstance();

    // 使用拷贝构造函数创建新实例
    Singleton* s2 = new Singleton(*s1);

    // 使用拷贝赋值运算符创建新实例
    Singleton* s3 = Singleton::getInstance();
    *s3 = *s1;

    // 使用移动构造函数创建新实例
    Singleton* s4 = new Singleton(std::move(*s1));

    // 使用移动赋值运算符创建新实例
    Singleton* s5 = Singleton::getInstance();
    *s5 = std::move(*s1);
#endif

int main(){
    return 0;
}

(2)版本二

cpp 复制代码
#include <memory>
#include <iostream>
using namespace std;

class Singleton {
public:
    static shared_ptr<Singleton> GetInstance() {
        if (!_instance) {
            _instance = shared_ptr<Singleton>(new Singleton());
        }
        return _instance;
    }

    // 将析构函数改为公有
    ~Singleton() {};

private:
    Singleton() {};

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;

    // 声明 shared_ptr 为友元,这样智能指针就能够访问类中的私有成员
    friend shared_ptr<Singleton>;

    static shared_ptr<Singleton> _instance;
};

shared_ptr<Singleton> Singleton::_instance = NULL;

int main() {
    shared_ptr<Singleton> instance = Singleton::GetInstance();

    // 在程序结束时会自动调用析构函数释放资源
    return 0;
}

//该智能指针的作用是确保singleton类的实例在不需要的时候能够正确释放内存,避免内存泄漏。
//同时,share_ptr智能指针还可以方便的管理类的生命周期,因为当所有指向该实例的智能指针都超出作用域的时候,实例会被自动销毁
//如果只有一个智能指针独享这个实例,那就用unique_ptr

//代码中没有使用 `std::make_shared`,而是直接使用 `new` 关键字来创建 `Singleton` 对象并将其包装在 `std::shared_ptr` 中。虽然 `std::make_shared` 在很多情况下是推荐的方式,但直接使用 `new` 也是有效的。在某些情况下,直接使用 `new` 可能更适合,例如需要传递额外的参数给构造函数时。

//在你的代码中,`Singleton::GetInstance()` 方法负责返回 `Singleton` 类的实例,并在需要时创建该实例。在 `main()` 函数中,你获取了 `Singleton` 的实例并存储在 `shared_ptr` 中,当程序结束时,`shared_ptr` 会调用析构函数来释放资源。
//总的来说,尽管没有使用 `std::make_shared`,你的代码仍然是有效的并且实现了单例模式。使用 `std::make_shared` 主要是为了避免直接使用 `new` 和 `delete`,从而提高代码的安全性和可读性。

//如果不使用智能指针,见singleton3.cpp

(3)版本三

cpp 复制代码
//那么对于之前的问题(析构函数不调用),我们就使用atexit()函数,在程序退出的时候调用传入的函数

#include<iostream>
#include<cstdlib>
using namespace std;

class singleton{
public:
    static singleton* GetInstance(){
        if(_instance==NULL){
            _instance=new singleton();
            atexit(Destructor);
        }
        return _instance;
    }

private:
    static void Destructor(){
        if(_instance!=NULL){
            delete _instance;
            _instance=NULL;
        }
    }

    singleton(){};
    ~singleton(){};
    //单例模式,就是一个类中只允许初始化一个实例,那就要禁用其他
    singleton(const singleton&)=delete;
    singleton& operator=(const singleton&)=delete;
    singleton(singleton&&)=delete;
    singleton& operator=(singleton&&)=delete;
    static singleton *_instance;
};

singleton *singleton::_instance=NULL;

int main(){
    return 0;
}

//但是这个代码又面临一个问题,就是单线程

(4)版本四

cpp 复制代码
#include<iostream>
#include<mutex>
using namespace std;

class singleton{
public:
    static singleton* GetInstance(){
        if(_instance==NULL){      //RAII 类的声明周期进行资料管理  双重检测
            lock_guard<std::mutex> lock(_mutex);  //当创建的时候,多线程可能会同时访问资源,这个时候就需要加锁
            if(_instance==NULL){
                _instance=new singleton();
                atexit(Destructor);
            }
        }
        return _instance;
    }

private:
    static void Destructor(){
        if(_instance!=NULL){
            delete _instance;
            _instance=NULL;
        }
    } 
    singleton(){};
    ~singleton(){};
    //单例模式,就是一个类中只允许初始化一个实例,那就要禁用其他
    singleton(const singleton&)=delete;
    singleton& operator=(const singleton&)=delete;
    singleton(singleton&&)=delete;
    singleton& operator=(singleton&&)=delete;
    static singleton *_instance;
    static mutex _mutex;
};

int main(){
    return 0;
}

//在c++98版本,代码的语义是基于单线程的,而现在出现了多核cpu
//而在c++11会对代码进行优化,例如编译器重排,cpu重排,可能会违反顺序一致性(前面的代码会在后面的代码之前执行),目的是让代码执行的速度更快
//因此会出现几种问题,比如可见性问题执行序问题。而在c++11中提供了解决办法,同步原语(原子变量和内存栅栏)
//见singleton.cpp5

(5)版本五

cpp 复制代码
#include<iostream>
#include<mutex>
#include<atomic>
using namespace std;

//load可以看见其他线程最新操作的数据,store修改数据让其他线程可见
//memory_order_acquire和memory_order_release之间所夹的代码既不会被优化到上面,也不会被优化到下面
//
class singleton{
public:
    static singleton* GetInstance(){
        singleton *tmp=_instance.load(memory_order_relaxed);  //这个_instance定义是原子变量,就代表这条语句不可拆分,load是可见,获取最新数据,而relaxed指松散型可以随便优化
        atomic_thread_fence(memory_order_acquire);   //内存栅栏解决了可见性问题和执行序问题,在这里使用,如果在构造函数里还使用了原子变量,就可以使用relaxed,因为这个是最快的
        //内存栅栏防止后面的代码编译到前面的代码的前面,保证了之前的读操作对后面的读操作可见
        if(tmp==NULL){
            lock_guard<mutex> lock(_mutex);  //即使是原子操作,也无法完全替代互斥锁的作用。原子操作可以确保数据的一致性和可见性,但并不能解决并发访问带来的竞态条件。
            tmp=_instance.load(memory_order_relaxed);
            if(tmp==NULL){
                tmp=new singleton;
            }

            atomic_thread_fence(memory_order_release);
            _instance.store(tmp,memory_order_relaxed);
            atexit(Destructor);
        }
        return tmp;
    }
private:
    static void Destructor(){
        singleton* tmp=_instance.load(memory_order_relaxed);
        if(tmp!=NULL){
            delete tmp;       
        }
    }
    singleton(){};
    ~singleton(){};
    //单例模式,就是一个类中只允许初始化一个实例,那就要禁用其他
    singleton(const singleton&)=delete;
    singleton& operator=(const singleton&)=delete;
    singleton(singleton&&)=delete;
    singleton& operator=(singleton&&)=delete;
    static atomic<singleton*> _instance;
    static mutex _mutex;
};

atomic<singleton*> singleton::_instance;
mutex singleton::_mutex;

int main(){
    return 0;
}

//这个代码虽然霸气,但是有些复杂

(6)版本六

cpp 复制代码
//上一个版本的代码太长了,接下来的代码直接使用静态全局变量
//这个代码主要利用了c++11的magic static特性:如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束
//C++11引入了Magic Statics特性,它允许在函数内部的静态变量第一次被使用时进行初始化,而不是在程序启动时。这种特性的好处是可以延迟静态变量的初始化,直到它们被需要为止,从而提高程序的性能和效率。
#include<iostream>
using namespace std;

class singleton{
public:
    static singleton& GetInstance(){
        static singleton instance;
        return instance;
    }
private:
    singleton(){};
    ~singleton(){};
    //单例模式,就是一个类中只允许初始化一个实例,那就要禁用其他
    singleton(const singleton&)=delete;
    singleton& operator=(const singleton&)=delete;
    singleton(singleton&&)=delete;
    singleton& operator=(singleton&&)=delete;
};

int main(){
    return 0;
}

//下一个版本使用类模板,提高代码的复用性

(7)版本七

cpp 复制代码
#include<iostream>
using namespace std;

template<typename T>
class singleton{
public:
    static T& GetInstance(){
        static T instance;
        return instance;
    }
protected:      //为了让子类能够调用得到父类中的构造和析构函数,必须放在protected里面
    virtual ~singleton(){}
    singleton(){} 
private:
    singleton(const singleton&)=delete;
    singleton& operator=(const singleton&)=delete;
    singleton(singleton&&)=delete;
    singleton& operator=(singleton&&)=delete;
};

class DesignPattern:public singleton<DesignPattern>{
   friend class singleton<DesignPattern>;   //由于子类继承的父类中使用模板构建子类实例,所以必须让父类可以使用子类的构造函数,所以这里必须声明友元
private:
    DesignPattern(){}
    ~DesignPattern(){}
};

int main(){
    DesignPattern& instance = DesignPattern::GetInstance();
    return 0;
}

5.工厂方法

定义一个用于创建对象的接口,让子类决定实例化哪一个类。 Factory Method使得一个类的实例化延迟到子类。

工厂模式是一种创建型设计模式,它提供了一种创建对象的接口,但允许子类决定实例化哪个类。工厂模式通过定义一个创建对象的接口,将对象的实例化过程封装在工厂类中,从而实现对象的创建和使用代码的分离。

工厂模式解决了以下问题:

  1. **封装对象的创建过程**:工厂模式将对象的实例化过程封装在工厂类中,客户端代码只需要关注如何使用对象,而无需关心对象的创建过程。

  2. **解耦对象的使用和创建**:工厂模式将对象的创建和使用解耦,客户端代码不需要直接依赖具体的对象类,只需要通过工厂类来获取所需的对象实例。

  3. **支持代码扩展**:工厂模式通过定义一个创建对象的接口,可以轻松地扩展和添加新的对象类型,而不需要修改现有的客户端代码。

  4. **隐藏对象的具体实现**:工厂模式可以隐藏对象的具体实现细节,只暴露给客户端一个抽象的接口,从而提高代码的安全性和可维护性。

总的来说,工厂模式适用于当客户端需要根据不同的条件或参数来创建不同类型的对象时,或者当对象的创建过程比较复杂时。通过工厂模式,可以实现对象的动态创建、对象的实例化过程的封装,以及客户端与具体对象类的解耦,从而提高代码的灵活性和可维护性。

cpp 复制代码
//把工厂类当做客户端暴漏给用户,创建新产品类的代码封装在工厂类中,避免在使用对象的地方出现大量的创建逻辑代码

#include <iostream>
#include <memory>

// 产品类
class Product {
public:
    virtual void operation() = 0;
};

// 具体产品类 A
class ConcreteProductA : public Product {
public:
    void operation() override {
        std::cout << "ConcreteProductA operation" << std::endl;
    }
};

// 具体产品类 B
class ConcreteProductB : public Product {
public:
    void operation() override {
        std::cout << "ConcreteProductB operation" << std::endl;
    }
};

// 工厂类
class Factory {
public:
    std::shared_ptr<Product> createProduct(int type) {
        switch (type) {
            case 1:
                return std::make_shared<ConcreteProductA>();
            case 2:
                return std::make_shared<ConcreteProductB>();
            default:
                return nullptr;
        }
    }
};

int main() {
    Factory factory;
    
    // 创建产品 A
    std::shared_ptr<Product> productA = factory.createProduct(1);
    productA->operation();
    
    // 创建产品 B
    std::shared_ptr<Product> productB = factory.createProduct(2);
    productB->operation();

    return 0;
}

6.抽象工厂

提供一个接口,让该接口负责创建一系列"相关或者相互依赖的 对象",无需指定它们具体的类。

抽象工厂模式是一种创建型设计模式,它提供了一种创建一系列相关或依赖对象的接口,而无需指定具体类。在抽象工厂模式中,客户端通过调用工厂接口来创建一组相关的产品对象,而不需要关心具体的产品类。这种方式可以使系统更具灵活性,能够轻松切换整个产品家族而不影响客户端代码。

抽象工厂模式通常涉及以下角色:

  1. **抽象工厂(Abstract Factory):** 定义了创建产品对象的接口,包括一组创建不同类型产品的方法。

  2. **具体工厂(Concrete Factory):** 实现了抽象工厂接口,在具体工厂中实现了创建一组相关产品的方法。

  3. **抽象产品(Abstract Product):** 定义了产品对象的接口,具体产品类将实现这些接口。

  4. **具体产品(Concrete Product):** 实现了抽象产品接口的具体产品类。

抽象工厂模式主要解决以下问题:

  1. **封装变化:** 抽象工厂模式将对象的创建过程封装在工厂接口中,客户端不需要了解具体的产品类,从而使客户端代码与具体产品类解耦,可以灵活地替换整个产品家族。

  2. **产品族的一致性:** 抽象工厂模式确保创建的产品是属于同一产品族的,保持产品之间的一致性,例如在创建界面元素时,保持按钮、文本框等元素的风格统一。

  3. **易于扩展:** 当需要添加新的产品族时,只需要创建新的具体工厂类和具体产品类,而不需要修改现有代码,符合开闭原则。

总的来说,抽象工厂模式提供了一种创建一组相关对象的接口,帮助我们在需要创建多个相关对象时保持代码的灵活性和可扩展性。

cpp 复制代码
#include <iostream>
/
// 抽象形状类
class Shape {
public:
    virtual void draw() = 0;
};

// 具体形状类:矩形
class Rectangle : public Shape {
public:
    void draw() override {
        std::cout << "Inside Rectangle: draw() method" << std::endl;
    }
};

// 具体形状类:圆形
class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Inside Circle: draw() method" << std::endl;
    }
};
/
// 抽象颜色类
class Color {
public:
    virtual void fill() = 0;
};

// 具体颜色类:红色
class Red : public Color {
public:
    void fill() override {
        std::cout << "Inside Red: fill() method" << std::endl;
    }
};

// 具体颜色类:蓝色
class Blue : public Color {
public:
    void fill() override {
        std::cout << "Inside Blue: fill() method" << std::endl;
    }
};
/
// 抽象工厂类
class AbstractFactory {
public:
    virtual Shape* create_shape() = 0;
    virtual Color* create_color() = 0;
};

// 具体形状工厂类
class ShapeFactory : public AbstractFactory {
public:
    Shape* create_shape() override {
        return new Rectangle();
    }

    Color* create_color() override {
        return nullptr; // 不负责创建颜色对象
    }
};

// 具体颜色工厂类
class ColorFactory : public AbstractFactory {
public:
    Shape* create_shape() override {
        return nullptr; // 不负责创建形状对象
    }

    Color* create_color() override {
        return new Blue();
    }
};

int main() {
    AbstractFactory* shape_factory = new ShapeFactory();
    Shape* rectangle = shape_factory->create_shape();
    rectangle->draw();

    AbstractFactory* color_factory = new ColorFactory();
    Color* blue = color_factory->create_color();
    blue->fill();

    delete rectangle;
    delete blue;
    delete shape_factory;
    delete color_factory;

    return 0;
}

7.责任链

使多个对象都有机会处理请求,从而避免请求的发送者和接收 者之间的耦合关系。将这些对象连成一条链,并沿着这条链传 递请求,直到有一个对象处理它为止。

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的直接耦合关系。在责任链模式中,将请求沿着一个处理链传递,直到有一个对象处理该请求为止。每个处理者对象都包含对下一个处理者对象的引用,请求在处理链上传递,直到有处理者能够处理请求或者请求达到链的末端。

责任链模式通常涉及以下角色:

  1. **抽象处理者(Handler):** 定义了处理请求的接口,包含一个指向下一个处理者的引用.

  2. **具体处理者(Concrete Handler):** 实现了抽象处理者接口,负责处理请求,如果自己无法处理,则将请求传递给下一个处理者。

责任链模式主要解决以下问题:

  1. **解耦请求发送者和接收者:** 责任链模式通过将请求发送者和接收者解耦,使得发送者无需知道具体的处理者,只需将请求发送给第一个处理者,由处理者链自行决定谁来处理请求。

  2. **动态指定处理者:** 可以动态改变处理链,新增或移除处理者,灵活调整处理流程,而不需要修改客户端代码。

  3. **处理者无需知道整个处理流程:** 每个处理者只需关注自己能否处理请求,不需要了解整个处理流程,增强了模块化和可维护性。

总的来说,责任链模式提供了一种将请求沿着处理链传递的机制,使得多个对象都有机会处理请求,从而实现请求的发送者和接收者之间的解耦,提高系统的灵活性和可扩展性。

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 抽象处理者
class Handler {
public:
    virtual void handleRequest(const std::string request) = 0;
    virtual void setNextHandler(Handler* next) = 0;
};

// 具体处理者 A
class ConcreteHandlerA : public Handler {
private:
    Handler* nextHandler;

public:
    ConcreteHandlerA() : nextHandler(nullptr) {}         //初始值必须置空
    void handleRequest(const std::string request) override {
        if (request == "A") {
            std::cout << "ConcreteHandlerA handles the request." << std::endl;
        } else if (nextHandler != NULL) {
            nextHandler->handleRequest(request);
        } else {
            std::cout << "No handler available for the request." << std::endl;
        }
    }

    void setNextHandler(Handler* next) override {
        nextHandler = next;
    }
};

// 具体处理者 B
class ConcreteHandlerB : public Handler {
private:
    Handler* nextHandler;

public:
    ConcreteHandlerB() : nextHandler(nullptr) {}         
    void handleRequest(const std::string request) override {
        if (request == "B") {
            std::cout << "ConcreteHandlerB handles the request." << std::endl;
        } else if (nextHandler != NULL) {
            nextHandler->handleRequest(request);
        } else {
            std::cout << "No handler available for the request." << std::endl;
        }
    }

    void setNextHandler(Handler* next) override {
        nextHandler = next;
    }
};

int main() {
    ConcreteHandlerA handlerA;
    ConcreteHandlerB handlerB;

    handlerA.setNextHandler(&handlerB);

    handlerA.handleRequest("A");
    handlerA.handleRequest("B");
    handlerA.handleRequest("C");

    return 0;
}

8.装饰器

动态地给一个对象增加一些额外的职责。就增加功能而言,装 饰器模式比生产子类更为灵活。

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许在不改变现有对象结构的情况下,动态地将新行为附加到对象上。该模式通过创建一个包装类(装饰器),来包裹原始的类,然后在不影响原始类结构的情况下,增加新的功能或责任。

装饰模式的主要目的是为了:

  1. **动态地扩展对象的功能**:通过使用装饰器,可以在运行时动态地为对象添加新的行为,而无需修改现有代码。

  2. **避免类爆炸**:装饰模式避免了创建大量子类来处理各种组合情况,从而避免类的数量急剧增加。

装饰模式通常用于以下情况:

  • 当需要在不修改现有代码的情况下,动态地添加新功能或责任时。

  • 当需要对对象的行为进行多次扩展,而子类的组合会导致类爆炸时。

  • 当类的功能需要被分离成不同的层次时,可以使用装饰模式来实现这种分层。

总的来说,装饰模式提供了一种灵活的方式来动态地添加功能,同时避免了代码修改和维护的复杂性。它使得代码更具扩展性和可维护性。

cpp 复制代码
#include <iostream>

// Component
class Coffee {
public:
    virtual void brew() = 0;
};

// ConcreteComponent
class SimpleCoffee : public Coffee {
public:
    void brew() override {
        std::cout << "Brewing simple coffee" << std::endl;
    }
};

// Decorator
class CoffeeDecorator : public Coffee {
protected:
    Coffee* decoratedCoffee;

public:
    CoffeeDecorator(Coffee* coffee) : decoratedCoffee(coffee) {}

    void brew() override {
        decoratedCoffee->brew();
    }
};

// ConcreteDecorator
class MilkDecorator : public CoffeeDecorator {
public:
    MilkDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}

    void brew() override {
        CoffeeDecorator::brew();
        addMilk();
    }

    void addMilk() {
        std::cout << "Adding milk" << std::endl;
    }
};

#if 0
在我之前提供的示例代码中,装饰模式的具体实现如下:

- `Coffee` 是组件接口,定义了咖啡的基本行为。

- `SimpleCoffee` 是具体组件,实现了 `Coffee` 接口,表示简单的咖啡。

- `CoffeeDecorator` 是装饰器类,实现了 `Coffee` 接口,并持有一个 `Coffee` 对象的引用,在 `brew()` 方法中调用被装饰对象的 `brew()` 方法。

- `MilkDecorator` 是具体装饰器类,继承了 `CoffeeDecorator`,在 `brew()` 方法中扩展了添加牛奶的功能。

在这个示例中,`MilkDecorator` 类包裹了 `SimpleCoffee` 类。通过将 `SimpleCoffee` 对象传递给 `MilkDecorator` 的构造函数,我们可以在不改变 `SimpleCoffee` 类的情况下,动态地为其添加额外的功能(添加牛奶)。
#endif

int main() {
    Coffee* simpleCoffee = new SimpleCoffee();
    Coffee* coffeeWithMilk = new MilkDecorator(simpleCoffee);

    coffeeWithMilk->brew();

    delete simpleCoffee;
    delete coffeeWithMilk;

    return 0;
}

9.组合模式

将对象组合成树型结构以表示"部分-整体"的层次结构。组合模 式使得用户对单个对象和组合对象的使用具有一致性。

组合模式(Composite Pattern)是一种结构型设计模式,它允许客户端统一处理单个对象和对象组合(包含子对象)的情况,使得客户端在处理单个对象和组合对象时具有一致性。组合模式将对象组织成树形结构,其中叶节点表示单个对象,而分支节点表示对象组合。

组合模式的主要目的是:

  1. **统一处理对象和对象组合**:组合模式允许客户端以统一的方式处理单个对象和对象组合,无需区分它们。

  2. **简化客户端代码**:客户端代码不需要关心对象是单个对象还是组合对象,从而简化了客户端代码的复杂性。

  3. **递归组合结构**:通过组合模式,可以递归地组合对象,形成树形结构,使得处理复杂的嵌套结构变得简单。

组合模式通常用于以下情况:

  • 当对象具有树形结构,并且需要以统一的方式处理单个对象和对象组合时。

  • 当希望客户端代码能够对单个对象和组合对象进行一致的操作时。

  • 当需要构建包含对象组合的层次结构,并希望以递归方式处理这种结构时。

总的来说,组合模式通过将对象组织成树形结构,使得客户端可以一致地处理单个对象和对象组合,从而简化了代码的复杂性并提高了代码的可维护性。

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

class Component {
public:
    virtual void operation() = 0;
    virtual void add(Component* component) {}
    virtual void remove(Component* component) {}
    virtual Component* getChild(int index) { return nullptr; }
};

class Leaf : public Component {
public:
    void operation() override {
        std::cout << "Leaf operation\n";
    }
};


class Composite : public Component {
private:
    std::vector<Component*> children;

public:
    void operation() override {
        std::cout << "Composite operation\n";
        for (Component* child : children) {
            child->operation();
        }
    }

    void add(Component* component) override {
        children.push_back(component);
    }

    void remove(Component* component) override {
        // Remove the component from children
    }

    Component* getChild(int index) override {
        if (index < children.size()) {
            return children[index];
        }
        return nullptr;
    }
};

int main() {
    Composite* composite = new Composite();
    composite->add(new Leaf());
    
    Composite* subComposite = new Composite();
    subComposite->add(new Leaf());
    subComposite->add(new Leaf());
    composite->add(subComposite);

    composite->operation();

    delete composite;
    
    return 0;
}

0voice · GitHub

相关推荐
yangyang_z3 小时前
【C++设计模式之Template Method Pattern】
设计模式
z26373056115 小时前
六大设计模式--OCP(开闭原则):构建可扩展软件的基石
设计模式·开闭原则
01空间11 小时前
设计模式简述(十八)享元模式
设计模式·享元模式
秋名RG14 小时前
深入理解设计模式之原型模式(Prototype Pattern)
设计模式·原型模式
Li小李同学Li1 天前
设计模式【cpp实现版本】
单例模式·设计模式
周努力.1 天前
设计模式之状态模式
设计模式·状态模式
268572592 天前
Java 23种设计模式 - 行为型模式11种
java·开发语言·设计模式
摘星编程2 天前
并发设计模式实战系列(19):监视器(Monitor)
设计模式·并发编程
yangyang_z2 天前
【C++设计模式之Strategy策略模式】
c++·设计模式·策略模式