Bootstrap

设计模式.

一、介绍

  • 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它旨在使代码更加可重用、易于理解和维护,并提高代码的可靠性。

二、六大原则

1、单一职责原则(Single Responsibility Principle, SRP)

  • 一个类应该只有一个引起它变化的原因,即一个类只负责一项功能或职责。
  • 如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或抑制该类完成其他职责的能力。因此,将类的职责分离到不同的类中,可以提高代码的可读性、可维护性和可扩展性。

2、开闭原则(Open-Closed Principle, OCP)

  • 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
  • 当需要增加新功能时,应该通过扩展已有的软件实体来实现,而不是修改已有的代码。这可以通过使用接口、抽象类或组合/聚合等方式来实现。

3、里氏替换原则(Liskov Substitution Principle, LSP)

  • 子类必须能够替换其基类而不会引起程序行为的改变。
  • 在软件设计中,应优先使用对象组合而不是类继承,以避免破坏基类已有的功能。子类应该实现父类的抽象方法,但不得重写父类的非抽象方法。子类可以增加自己特有的方法,并且子类方法的前置条件(输入参数)应该比父类方法的输入参数类型更宽松,方法的后置条件(返回值)应该比父类返回值类型更严格。

4、接口隔离原则(Interface Segregation Principle, ISP)

  • 客户端不应该依赖它不需要的接口,即使用多个隔离的接口比使用单个的接口好。
  • 一个接口应该只包含客户感兴趣的方法,而不是将所有可能的方法都放在一个接口中。这样可以降低类之间的耦合度,便于维护。

5、依赖倒置原则(Dependency Inversion Principle, DIP)

  • 高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
  • 这意味着我们应该通过抽象层(如接口或抽象类)来定义模块间的依赖关系,而不是直接依赖具体的实现类。依赖倒置原则本质上就是面向接口编程,它提高了系统的灵活性和可扩展性。

6、迪米特法则(Law of Demeter, LoD)

  • 一个实体应该尽量少的与其他实体发生相互作用,使得系统功能模块相对独立。
  • 迪米特法则也称为最少知道原则,它要求一个类应该尽量少的了解其他类的内部细节,只与必要的类进行交互。这样可以降低系统的复杂性,提高系统的可维护性和可扩展性。

三、单例模式

1、介绍

  • 单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。在需要控制资源访问、实现全局状态管理、或者在整个应用程序生命周期中需要保持某些对象唯一性的场景中非常有用。
关键点作用
私有构造函数防止外部通过 new 关键字创建实例
静态实例变量用于存储类的唯一实例
公共静态方法提供一个全局访问点来获取实例
删除拷贝构造和赋值运算符重载防止创建多个实例和对象间的赋值操作

2、模式

懒汉模式饿汉模式
创建时机第一次访问时类加载时
线程安全不安全,需要使用同步锁等方式来确保线程安全线程安全
优点延迟加载,节省资源实现简单,线程安全
缺点实现复杂,线程不安全无论是否需要使用都会占用资源,可能导致资源浪费

3、示例

(1)饿汉模式

#include <iostream>

class Singleton
{
private:
    static Singleton _eton;

private:
    Singleton() : _data(666) {  std::cout << "单例Singleton 饿汉, 构造函数" << std::endl;  };
    ~Singleton() {};
    Singleton(const Singleton &) = delete;
    Singleton &operator=(const Singleton &) = delete;

private:
    int _data;

public:
    static Singleton &getInstance()
    {
        return _eton;
    }

    int getData()
    {
        return _data;
    }
};

Singleton Singleton::_eton;

int main()
{
    std::cout << Singleton::getInstance().getData() <<std::endl;
    return 0;
}

在这里插入图片描述

(2)懒汉模式

class Singleton
{
private:
    Singleton() : _data(666) {  std::cout << "单例Singleton 懒汉, 构造函数" << std::endl;  };
    ~Singleton() {};
    Singleton(const Singleton &) = delete;
    Singleton &operator=(const Singleton &) = delete;

private:
    int _data;

public:
    static Singleton &getInstance()
    {
        static Singleton _eton;
        return _eton;
    }

    int getData()
    {
        return _data;
    }
};
  • 上方代码在C++11后才是线程安全的,因为C++11局部的静态变量的创建是线程安全的。

四、工厂模式

1、介绍

  • 工厂模式(Factory Pattern)是一种创建型设计模式,它旨在定义一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂模式让类的实例化推迟到其子类。这样可以将对象的实例化与客户端代码的耦合度降到最低,同时也提供了一种可扩展的方式来创建对象。
主要角色作用
抽象产品(AbstractProduct)定义产品的接口,是工厂方法模式所创建对象的公共父类
具体产品(ConcreteProduct)实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建
抽象工厂(AbstractFactory)在抽象工厂类中声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口
具体工厂(ConcreteFactory)实现了抽象工厂中声明的工厂方法,并可由客户端调用,返回一个具体产品类的实例

2、实现方式

  • 简单工厂模式(Simple Factory Pattern):通过一个工厂类来创建对象,客户端通过传递不同的参数给工厂类,工厂类根据参数的不同来创建不同的对象实例。优点是客户端代码简单,隐藏了具体对象的创建细节;缺点是当需要添加新的产品时,需要修改工厂类的代码,违反了开闭原则。
  • 工厂方法模式(Factory Method Pattern):将对象的创建延迟到子类中去实现,即定义一个创建对象的接口,但让子类决定实例化哪个类。客户端通过调用工厂方法来创建对象,具体的实例化过程由子类负责。这样可以解决简单工厂模式中的缺点,使得系统更具灵活性和可扩展性。
  • 抽象工厂模式(Abstract Factory Pattern):提供一个接口用于创建一系列相关或依赖对象的家族,而不需要指定具体的类。与工厂方法模式相比,抽象工厂模式是针对多个产品等级结构的,可以创建不同产品族的全部产品。这种模式适用于需要在运行时切换不同产品族的场景,但增加新的产品族往往不太容易。

3、示例

  • 简单工厂模式
#include <iostream>
#include <memory>
#include <string>

class Fruit
{
public:
    virtual void name() = 0;
};

class Apple : public Fruit
{
public:
    void name() override
    {
        std::cout << "i am a apple" << std::endl;
    }
};

class Pineapple : public Fruit
{
public:
    void name() override
    {
        std::cout << "i am a pineapple" << std::endl;
    }
};

class FruitFactory
{
public:
    static std::shared_ptr<Fruit> create(const std::string& name)
    {
        if(name == "apple")
            return std::make_shared<Apple>();
        else if(name == "pineapple")
            return std::make_shared<Pineapple>();
        return std::shared_ptr<Fruit>();
    }
};

int main()
{
    std::shared_ptr<Fruit> fruit = FruitFactory::create("apple");
    fruit->name();
    fruit.reset();
    fruit = FruitFactory::create("pineapple");
    fruit->name();
    return 0;
}

在这里插入图片描述

  • 工厂方法模式
class Factory
{
public:
    virtual std::shared_ptr<Fruit> create() = 0;
};

class AppleFactory : public Factory
{
public:
    std::shared_ptr<Fruit> create()
    {
        return std::make_shared<Apple>();
    }
};

class PineappleFactory : public Factory
{
public:
    std::shared_ptr<Fruit> create()
    {
        return std::make_shared<Pineapple>();
    }
};

int main()
{
    std::shared_ptr<Factory> fruitFactory(new AppleFactory());
    std::shared_ptr<Fruit> fruit = fruitFactory->create();
    fruit->name();
    fruitFactory.reset(new PineappleFactory());
    fruit = fruitFactory->create();
    fruit->name();

    return 0;
}

在这里插入图片描述

  • 抽象工厂模式
class Animal
{
public:
    virtual void voice() = 0;
};

class Dragon : public Animal
{
public:
    void voice() override
    {
        std::cout << "恶龙咆哮" << std::endl;
    }
};

class Wolf : public Animal
{
public:
    void voice() override
    {
        std::cout << "我一定会回来的" << std::endl;
    }
};


class Factory
{
public:
    virtual std::shared_ptr<Fruit> getFruit(const std::string& name) = 0;
    virtual std::shared_ptr<Animal> getAnimal(const std::string& name) = 0;
};

class FruitFactory : public Factory
{
public:
    std::shared_ptr<Fruit> getFruit(const std::string& name) override
    {
        if(name == "apple")
            return std::make_shared<Apple>();
        else if(name == "pineapple")
            return std::make_shared<Pineapple>();
        return std::shared_ptr<Fruit>();        // 需用shared_ptr
    }

    std::shared_ptr<Animal> getAnimal(const std::string& name)  override
    {
        return std::shared_ptr<Animal>();        // 需用shared_ptr
    }
};

class AnimalFactory : public Factory
{
public:
    std::shared_ptr<Fruit> getFruit(const std::string& name)  override
    {
        return std::shared_ptr<Fruit>();
    }

    std::shared_ptr<Animal> getAnimal(const std::string& name) override
    {
        if(name == "dragon")
            return std::make_shared<Dragon>();
        else if(name == "wolf")
            return std::make_shared<Wolf>();
        return std::shared_ptr<Animal>();
    }
};

class FactoryProducer
{
public:
    static std::shared_ptr<Factory> getFactory(const std::string& name)     // 可以声明为静态,这样调用时就不用创建对象
    {
        if(name == "fruit")
            return std::make_shared<FruitFactory>();
        else if(name == "animal")
            return std::make_shared<AnimalFactory>();
        return std::shared_ptr<Factory>();
    }
};


int main()
{
    std::shared_ptr<Factory> factory = FactoryProducer::getFactory("fruit");
    std::shared_ptr<Fruit> fruit = factory->getFruit("apple");
    fruit->name();
    fruit.reset();
    fruit = factory->getFruit("pineapple");
    fruit->name();
    std::cout << "\n";

    factory = FactoryProducer::getFactory("animal");
    std::shared_ptr<Animal> animal = factory->getAnimal("dragon");
    animal->voice();
    animal.reset();
    animal = factory->getAnimal("wolf");
    animal->voice();

    return 0;
}

在这里插入图片描述

五、建造者模式

1、介绍

  • 建造者模式(Builder Pattern)是一种创建型设计模式,它将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。该模式允许用户通过指定复杂对象的类型和内容来构建它们,而无需知道内部的具体构建细节。
  • 建造者模式的目的是将对象的构建过程拆分成多个简单的步骤,并允许用户通过调用这些步骤来逐步构建对象。
主要角色说明
产品(Product)要创建的复杂对象
抽象建造者(Builder)定义一个用于创建和装配产品各个部件的抽象接口
具体建造者(ConcreteBuilder)实现抽象建造者接口,并构建和装配具体产品的各个部件。同时,它还提供一个方法来返回最终创建的产品
指挥者(Director)构造一个使用抽象建造者接口的对象。它主要用于创建一个复杂对象,并控制其构建过程。指挥者并不与具体的产品类发生关系,而是通过一个抽象建造者接口来间接创建产品
  • 与工厂模式的区别:建造者模式与工厂模式在创建对象方面有所不同。工厂模式主要用于创建具有共同接口或基类的不同实例,而建造者模式则更关注于对象的复杂构建过程和各个部件的组装。

2、实现方式

  • 在建造者模式中,通常会有一个具体的产品类,该类包含了产品的各个属性和方法。然后,会有一个或多个具体建造者类,这些类实现了抽象建造者接口,并提供了构建产品各个部件的方法。指挥者类则负责调用这些方法来逐步构建产品。

3、示例

#include <iostream>
#include <string>
#include <memory>

class Computer
{
public:
    void setBoard(const std::string& board)       // 派生类不重写,不要加virtual
    {
        _board = board;             // 没加virtual,需要对成员变量进行赋值
    }
    void setDisplay(const std::string& display)
    {
        _display = display;
    }
    virtual void setOs() = 0;

    std::string show()
    {
        // std::cout<<_board<<" "<<_display<<std::endl;
        std::string comp = "computer:\n ";          // 换行之和多加一个空格,否则下一句中文可能乱码
        comp += "\tboard: " + _board + " \n ";
        comp += "\tdisplay: " + _display + "\n ";
        comp += "\tos: " + _os + "\n";
        // std::cout << comp << std::endl;
        return comp;
    }
protected:
    std::string _board;
    std::string _display;
    std::string _os;
};

class Legion : public Computer
{
public:
    void setOs()
    {
        _os = "Windows 11";
    }
};

class Builder
{
public:
    virtual void buildBoard(const std::string& board) = 0;
    virtual void buildDisplay(const std::string& display) = 0;
    virtual void buildOs() = 0;     // os也需要调用
    virtual std::shared_ptr<Computer> build() = 0;
};

class LegionBuilder : public Builder
{
public:
    LegionBuilder():_cp(new Legion()){}         // 构造对象
    void buildBoard(const std::string& board)
    {
        _cp->setBoard(board);
    }

    void buildDisplay(const std::string& display)
    {
        _cp->setDisplay(display);
    }

    void buildOs()
    {
        _cp->setOs();
    }

    std::shared_ptr<Computer> build()
    {
        return _cp;
    }

private:
    std::shared_ptr<Computer> _cp;        // 类型需要是Computer
};

class Direct
{
public:
    Direct(Builder* bp):_bp(bp){}

    void construct(const std::string& board, const std::string& display)
    {
        _bp->buildBoard(board);
        _bp->buildDisplay(display);
        _bp->buildOs();
    }
private:
    std::shared_ptr<Builder> _bp;
};

int main()
{
    Builder* bp = new LegionBuilder();
    std::unique_ptr<Direct> dup(new Direct(bp));
    dup->construct("联想", "三星");
    std::shared_ptr<Computer> cp = bp->build();
    std::cout << cp->show();
    // cp->show();

    return 0;
}

在这里插入图片描述

六、代理模式

1、介绍

  • 代理模式(Proxy Pattern)是程序设计中的一种结构型设计模式,其为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
  • 代理模式的目的是在不修改目标对象代码的前提下,通过代理对象来控制对目标对象的访问,并增加额外的功能或提供间接访问。

2、主要角色

  • 抽象主题(Subject):声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题。
  • 真实主题(RealSubject):实现了抽象主题接口,是真正执行任务的对象。
  • 代理主题(Proxy):代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题;控制真实主题的应用,负责在需要的时候创建真实主题对象(和删除真实主题对象)。代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯的将调用传递给真实主题对象。

3、示例

#include <iostream>

class RentHouse
{
public:
    virtual void rentHouse() = 0;
};

class Landlord : public RentHouse
{
public:
    void rentHouse()
    {
        std::cout << "Landlord rent the house" << std::endl;
    }
};

class Intermediary : public RentHouse
{
public:
    void rentHouse()
    {
        std::cout << "发布租房起始" << std::endl;
        std::cout << "带领租客看房" << std::endl;
        _ld.rentHouse();
        std::cout << "后期维修相关" << std::endl;
    }
private:
    Landlord _ld;
};

int main()
{
    Intermediary im;
    im.rentHouse();

    return 0;
}

在这里插入图片描述

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
本文只是在编写项目,学习过程中所做的总结,不会涉及过深的概念
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕

;