【C++】设计模式

文章目录

  • 设计模式
    • 设计模式六大原则
    • 单例模式
    • 工厂模式
    • 建造者模式
    • 代理模式

设计模式

设计模式是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它是一套提高代码复用性,可维护性,可读性,稳健性以及安全性的解决方案

设计模式六大原则

  • 单一责任原则(Single Responsibility Principle)

    • 类的职责盈感单一,一个方法只做一件事,职责划分清晰,每次改动到最小单位的方法或类
    • 使用建议:两个完全不一样的功能不应该放在一个类中,一个类中应该是一组相关性很高的函数、数据的封装
    • 用例:网络聊天类(❌)应该分割成网络通信类 + 聊天类
  • 开闭原则(Open Closed Principle)

    • 对扩展开放,对修改封闭(只添加新功能,不修改原有内容)
    • 使用建议:对软件实体的改动,最好用扩展而非修改的方式
    • 用例:超时卖货:商品价格—不是修改商品原来的价格,而是新增促销的价格
  • 里氏替换原则(Liskov Substitution Principle)

    • 凡事父类能够出现的地方,子类就可以出现,而且替换为子类也不会出现任何的错误或者异常
    • 在继承类时,务必重写父类中的所有方法,尤其注意父类的protected方法,子类尽量不要暴露自己的public方法供外界调用
    • 使用建议:子类无比完全实现父类的方法,还子类可以有自己的个性,覆盖或者实现父类的方法时,输入的参数可以被放大,输出也可以缩小
    • 用例:跑步运动员类:会跑步, 子类长跑运动员-会跑步且擅长长跑,子类短跑运动员:会跑步且擅长短跑
  • 依赖倒置原则(Dependence Inversion Principle)

    • 高层模块不应该依赖底层模块,两者都应该依赖其抽象,不可分隔的原子逻辑就是低层的模式,原子逻辑组装成的就是高层模块
    • 模块间依赖通过抽象(接口)发生,具体类之间不能直接依赖
    • 使用建议:每一个类都尽量有抽象类,任何类都不应该从具体类派生。尽量不要重写基类的方法。结合里氏替换原则使用
    • 用例:奔驰车司机 – 只能开奔驰,司机类:给什么车开什么车 : 开车的人 : 司机 – 依赖抽象
  • 迪米特法则(Law of Demeter) 最少知道法则

    • 尽量减少对象之间的交互,从而减少类之间的耦合。一个对象应该对其他对象有最少的了解,对类的低耦合提出了明确的要求:
      • 只喝直接的朋友交流,朋友间也是有剧烈的。自己的就是自己的(如果一个方法放在本类中,既不增加类间关系,也不对本类造成负面影响,那就放置在本类中)
    • 用例:老师让班长点名,老师给班长名单,班长点名勾选,返回结果。老师只和班长交互,同学们只和班长交互
  • 接口隔离原则

    • 客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上
    • 使用建议:接口设计尽量精简单一,但是不要对外暴露没有啥意义的接口
    • 用例:修改密码,不应该提供用户信息接口,而是单一使用修改密码接口

从整体上理解六大设计原则,可以简要概括为一句话,用抽象构建框架,用实现扩展细节,具体到每一条设计原则,则对应一条注意事项

单例模式

/* 饿汉单例模式 以空间换时间 */
class Singleton{
public:
    static Singleton& getInstance() { return _eton; }
    int getData() { return _data; }
private:
    Singleton(int data = 99) : _data(data){}
    ~Singleton(){};
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
private:
    static Singleton _eton;
    int _data;
};
Singleton Singleton::_eton;

int main() {
    std::cout << Singleton::getInstance().getData() << std::endl;
    return 0;
}
/* 懒汉单例模式 懒加载 -- 延时加载思想 -- 一个对象用的时候再实例化 */
// 这里介绍 作者提出的一种更加优雅简便的单例模式 Meyers Singleton int C++
// C++11后是线程安全的

class Singleton{
public:
    static Singleton& getInstance() {
        static Singleton _eton;
        return _eton;
    }
    int getData() { return _data; }
private:
    Singleton(int data = 99) : _data(data){}
    ~Singleton() {};
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    int _data;
};

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

工厂模式

工厂模式是一种创建型的设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们创建对象不会对上层暴露创建逻辑,而是通过使用一个共同结构来指向新创建的对象,因此实现创建-使用的分离

工厂模式分为:

  • 简单工厂模式:简单工厂模式实现需要由一个工厂对象通过类型决定创建出来的制定产品类的实例。假设有个工厂可以生产水果,当客户需要产品时明确告知工厂生产哪种水果,工厂需要接收用户提供的类别信息,当新增产品的时候,工厂内部取添加新产品的生产方式
class Fruit{
public:
    virtual void name() = 0;
private:   
};

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

class Banana : public Fruit{
public:
    void name() override {
        std::cout << "I'm a banana" << std::endl;
    }
};

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

int main() {
    std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");
    fruit->name();
    fruit = FruitFactory::create("香蕉");
    fruit->name();
    return 0;
}


这个模式的结构和管理产品对象的方式非常简单,但是它的扩展性非常差,当我们需要新增产品的时候,就需要去修改工厂类新增一个类型的产品创造逻辑,违背了开闭原则

  • 工厂方法模式:在简单的工厂模式下新增了多个工厂,多个产品,每个产品对应一个工厂。假设现在有A、B两种产品,则开两个工厂,工厂A主要负责生产产品A,工厂B主要生产产品B,用户只要知道产品的工厂名,而不需要知道具体的产品信息,工厂不需要接收客户的产品类别,只负责生产产品
/* 工厂方法模式 */
class Fruit{
public:
    virtual void name() = 0;
private:   
};

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

class Banana : public Fruit{
public:
    void name() override {
        std::cout << "I'm a banana" << std::endl;
    }
};
class FruitFactory {
public:
    virtual std::shared_ptr<Fruit> createFruit() = 0;
};

class AppleFactory : public FruitFactory {
public:
    virtual std::shared_ptr<Fruit> createFruit() override {
        return std::make_shared<Apple>();
    }
};

class BananaFactory : public FruitFactory {
public:
    virtual std::shared_ptr<Fruit> createFruit() override {
        return std::make_shared<Banana>();
    }
};

int main() {
    std::shared_ptr<FruitFactory> ff(new AppleFactory());
    std::shared_ptr<Fruit> fruit1 = ff->createFruit();
    fruit1->name();
    ff.reset(new BananaFactory());
    std::shared_ptr<Fruit> fruit2 = ff->createFruit();
    fruit2->name();
    return 0;
}

工厂方法模式每次增减一个产品时,都需要增加一个具体的产品类和工厂类,这使得系统中类的个数成倍的增加,在一定程度上增加了系统的耦合度

  • 抽象工厂模式:工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必增加系统的开销,此时我们可以考虑将一些相关的产品组成一个产品族(位于不同产品等级结构中功能相互关联的产品组成的家族),由于一个工厂统一生产,这就是抽象工厂模式的基本思想
#include 
#include 

/* 简单工厂模式 */
class Fruit{
public:
    virtual void name() = 0;
private:   
};

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

class Banana : public Fruit{
public:
    void name() override {
        std::cout << "I'm a banana" << std::endl;
    }
};

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

class Lamp : public Animal {
    public:
        virtual void name() override { 
            std::cout << "I'm a Lamp" << std::endl;
        }
};

class Dog : public Animal {
    public:
        virtual void name() override {
            std::cout << "I'm  a dog" << 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:
    virtual std::shared_ptr<Fruit> getFruit(const std::string& name) override{
        if (name == "苹果") {
            return std::make_shared<Apple>();
        } else {
            return std::make_shared<Banana>();
        }
    }
    virtual std::shared_ptr<Animal> getAnimal(const std::string& name) override{
        return std::shared_ptr<Animal>();
    }
};

class AnimalFactory : public Factory {
    public:
    virtual std::shared_ptr<Fruit> getFruit(const std::string& name) override {
        return std::shared_ptr<Fruit>();
    }
    virtual std::shared_ptr<Animal> getAnimal(const std::string& name) override {
        if (name == "山羊") {
            return std::make_shared<Lamp>();
        } else {
            return std::make_shared<Dog>();
        }
    }
};

class FactoryProducer {
    public: 
        static std::shared_ptr<Factory> create(const std::string &name) {
            if (name == "水果") {
                return std::make_shared<FruitFactory>();
            } else {
                return std::make_shared<AnimalFactory>();
            }
        }
};

int main() {
    std::shared_ptr<Factory> ff = FactoryProducer::create("水果");
    std::shared_ptr<Fruit> fruit = ff->getFruit("苹果");
    fruit->name();
    fruit = ff->getFruit("香蕉");
    fruit->name();
    ff = FactoryProducer::create("动物");
    std::shared_ptr<Animal> animal = ff->getAnimal("山羊");
    animal->name();
    animal = ff->getAnimal("小狗");
    animal->name();
    return 0;
}

抽象工厂模式适用于生产多个工厂系列产品衍生的设计模式,增加新的产品等级结构复杂,需要对原有系统进行较大修改,甚至需要修改抽象层代码,违背了开闭原则

建造者模式

建造者模式是一种创建型的设计模式,使用多个简单对象一步一步构建成一个复杂的对象,能够将一个复杂的对象的构建与它的表示分离,提供一种创建对象的最佳方式。主要用于解决对象的构建过于复杂的问题

建造者模式主要基于四个核心实现:

  • 抽象产品类
  • 具体产品类:一个具体的产品对象类
  • 抽象Builder类:创建一个产品对象所需要的各个零部件的抽象接口
  • 具体产品的Builder类:实现抽象接口,构建各个部件
  • 指挥者Director类:统一组建过程,提供给调用者使用,通过指挥者来获取产品
#include 
#include 
#include 

/* 通过MacBook的构造理解建造者模式*/

class Computer{
    public:
        Computer(){};
        void setBoard(const std::string &board) { _board = board; }
        void setDisplay(const std::string &display) { _display = display; }
        virtual void setOs() = 0;
        void showParamaters() {
            std::string param = "Computer Paramaters: \n";
            param += "\tBoard: " + _board + "\n";
            param += "\tDispaly: " + _display + "\n";
            param += "\tOs: " + _os + "\n";
            std::cout << param << std::endl;
        }
    protected:
        std::string _board;
        std::string _display;
        std::string _os;
};
class MacBook : public Computer{
    public:
        virtual void setOs() override {
            _os = "Mac OS x12";
        }
};

class Builder {
    public:
        virtual void buildBoard(const std::string &board) = 0;
        virtual void buildDisplay(const std::string &display) = 0;
        virtual void buildOs() = 0;
        virtual std::shared_ptr<Computer> build() = 0;
};

class MacBookBuilder : public Builder{
    public:
        MacBookBuilder() : _computer(new MacBook()) {}
        void buildBoard(const std::string& board) {
            _computer->setBoard(board);
        }
        void buildDisplay(const std::string& display) {
            _computer->setDisplay(display);
        }
        void buildOs() {
            _computer->setOs();
        }
        std::shared_ptr<Computer> build() {
            return _computer;
        }
    private:
        std::shared_ptr<Computer> _computer;
};

class Director {
    public:
        Director(Builder* builder) : _builder(builder) {}
        void construct(const std::string& board, const std::string& display) {
            _builder->buildBoard(board);
            _builder->buildDisplay(display);
            _builder->buildOs();
        }
    private:
        std::shared_ptr<Builder> _builder;
};


int main() {
    Builder * builder = new MacBookBuilder();
    std::unique_ptr<Director> director(new Director(builder));
    director->construct("华硕主板", "三星显示器");
    std::shared_ptr<Computer> computer = builder->build();
    computer->showParamaters();
    return 0;
}

代理模式

代理模式指的是代理控制对其他对象的访问,也就是代理对象控制对原对象的引用。在某些情况下,一个对象不适合或者不能直接被引用访问,而代理对象可以在客户端和目标对象之间起到中介作用

代理模式的结构包括一个是真正的你要访问的目标对象(目标类)、一个是代理对象。目标对象与代理对象实现同一个接口,先访问代理类再通过代理类访问目标对象。代理模式一般分为静态代理、动态代理

  • 静态代理指的是,在编译时就已经确定好了代理类和被代理类的关系。也就是说,在编译时已经确定了代理要代理的是哪一个被代理类
  • 动态代理指的是,在运行时才动态生成代理类,并将其与被代理类绑定。这意味着,在运行时才能确定代理类要代理的是哪个被代理类

以租房为例,租客租房,中间经过房屋中介向房东租房,使用代理模式实现

#include 

/* 代理模式 */
class RentHouse {
    public:
        virtual void rentHouse() = 0;
};


class Landlord : public RentHouse {
    public:
        void rentHouse() {
            std::cout << "房子租出去了" << std::endl;
        }
};

class Intermediary : public RentHouse {
    public:
        void rentHouse() {
            std::cout << "发布招租启事" << std::endl;
            std::cout << "带人看房" << std::endl;
            _landload.rentHouse();
            std::cout << "负责租后维修" << std::endl;
        }
    private:
        Landlord _landload;
};

int main() {
    Intermediary intermediary;
    intermediary.rentHouse();
    return 0;
}

你可能感兴趣的:(C++学习,c++,设计模式,microsoft)