• C++设计模式



    模式设计的六大原则

    六大设计原则超详细介绍(再不理解你打我)

    单一职责原则

    ⼀个类应该仅有⼀个引起它变化的原因。

    解释: 一个类或函数只承担一个职责。
    比如一个用户类有一个修改账号和密码的成员函数,那这个成员函数就承担了两个职责:修改账号和修改密码,它们之间相互耦合。用户如果只想修改密码或者只想修改账号就得调用同一个函数,给调用增加了麻烦。如果后续增加修改邮箱的功能,是不是还得给这个函数增加修改邮箱的功能?

    class User1 {
    public:
    	//修改账号和密码
    	bool ModifyUserInformation(string information, int op){
    		//如果op为1,则修改账号
    		if (op == 1){
    
    		}
    		//op为2,修改密码
    		else if (op == 2){
    
    		}
    		//如果后续还要增加修改邮箱的功能,还需要传入op==3,这不仅不好维护
    		//而且也不方便程序员调用这个函数,因为并不知道op填多少
    	}
    private:
    	//用户的账号
    	string _user_name;
    	//用户的密码
    	string _user_password;
    	//用户的邮箱
    	string _user_email;
    };
    class User2 {
    public:
    	//修改账号
    	bool ModifyUserName(string new_name){
    		
    	}
    	//修改账号
    	bool ModifyUserPassword(string new_password) {
    
    	}
    	//如果后续增加修改邮箱的功能,只需要再加一个成员函数即可
    private:
    	//用户的账号
    	string _user_name;
    	//用户的密码
    	string _user_password;
    	//用户的邮箱
    	string _user_email;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    再举一个例子,比如一个抽象类“家务”有两个函数“洗碗”和“拖地”,另外两个用户类,一个类只负责“洗碗”,一个类只负责“拖地”。

    class HouseWork {
    public:
    	virtual void WashDishes() = 0;
    	virtual void Mopping() = 0;
    };
    class User1 : public HouseWork {
    public:
    	void WashDishes(){
    		cout << "用户1负责洗碗" << endl;
    	}
    	//因为父类是抽象类,所以User1必须要实现这个拖地的接口,即使它不负责拖地
    	void Mopping() {};
    };
    class User2 : public HouseWork {
    public:
    	void WashDishes() {};
    	void Mopping() {
    		cout << "用户2负责拖地" << endl;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    可以看到上面抽象类HouseWork有两个功能,User1和User2只需要其中一个功能时,还必须要实现另一个功能。并且如果HouseWork增加一个函数,比如做饭。那User1和User2就必须要再实现做饭这个函数,这是非常麻烦的。

    因此,我们可以把这些功能分离出来单独实现成一个类:

    class HouseWork {
    };
    //洗碗和拖地分离
    class Wash:public HouseWork {
    public:
    	virtual void WashDishes() = 0;
    };
    class Mop :public HouseWork{
    public:
    	virtual void Mopping() = 0;
    };
    //如果想增加做饭的接口
    class Cook :public HouseWork {
    public:
    	virtual void Cooking() = 0;
    };
    class User1 : public Wash {
    public:
    	void WashDishes() {
    		cout << "用户1负责洗碗" << endl;
    	}
    };
    class User2 : public Mop {
    public:
    	void Mopping() {
    		cout << "用户2负责拖地" << endl;
    	}
    };
    //User3负责拖地和做饭
    class User3 : public Mop,Cook {
    public:
    	void Mopping() {
    		cout << "用户3负责拖地" << endl;
    	}
    	void Cooking() {
    		cout << "用户3负责做饭" << endl;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    里氏替换原则

    子类在继承类时,必须重写父类中所有的抽象函数。子类可以实现自己的函数,但不能覆盖掉父类的非抽象函数。

    子类的对象能够替换其基类的对象被使用。

    解释: 前面很好理解,在继承时经常用:

    #include 
    //鸟
    class Bird {
    public:
        Bird() {}
        virtual ~Bird() {}
        virtual void Fly() {
            std::cout << "I am  a bird and I am flying" << std::endl;
        }
    };
    //燕子
    class Swallow : public Bird {
    public:
        Swallow() {}
        ~Swallow() {}
        void Fly() override {
            std::cout << "I am  a Swallow and I am flying" << std::endl;
        }
    };
    //大雁
    class WildGoose : public Bird {
    public:
        WildGoose() {}
        ~WildGoose() {}
        void Fly() override {
            std::cout << "I am  a Wild Goose and I am flying" << std::endl;
        }
    };
    //模拟鸟的飞行
    void Fly(Bird& b) {
        b.Fly();
    }
    
    int main() {
        WildGoose goose;
        Swallow s;
        Fly(s);
        Fly(goose);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    子类的对象能够替换其基类的对象被使用。比如:任何使用Animal抽象类的地方,我们都可以用其子类如Cat类替代,因为Cat类重写了Animal的各种抽象函数。

    依赖倒置原则

    模块间的依赖通过抽象发生,实现类之间不直接发生依赖关系,其依赖关系是通过接口或抽象类产生的;抽象不应该依赖具体实现,具体实现应该依赖于抽象类。

    解释: 直接通过例子来说明,现在有两个类,披萨类和用户类,用户可以执行吃披萨的动作:

    class Pizza {
    public:
    	void eated() {
    		cout << "披萨正在被吃" << endl;
    	}
    };
    class User {
    public:
    	void eat(Pizza food) {
    		cout << "用户正在吃饭" << endl;
    		food.eated();
    	}
    	//这样实现eat有很大的问题,比如后续如果增加吃汉堡的功能
    	//那还需要再实现一个eat函数
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以发现如果后续用户想要增加吃汉堡的功能,就得再写一个eat函数。为了解决这个问题,我们可以将它们之间的依赖关系抽象出来:

    //使用抽象类解决了Food和User的依赖关系
    class Food
    {
    public:
        virtual void eated() = 0;
    };
    class IUser
    {
    public:
        virtual void eat(Food *) = 0;
    };
    
    class Pizza : public Food
    {
    public:
        void eated()
        {
            cout << "披萨正在被吃" << endl;
        }
    };
    class Hamburger : public Food
    {
    public:
        void eated()
        {
            cout << "汉堡正在被吃" << endl;
        }
    };
    class User1 : public IUser
    {
    public:
        void eat(Food *food)
        {
            cout << "用户正在吃饭" << endl;
            food->eated();
            //后续如果想增加吃其他食物的功能,比如吃饺子
            //直接实现一个饺子类即可,不需要再写一个eat函数
        }
    };
    int main()
    {
        User1 u1;
        u1.eat(new Pizza);
        u1.eat(new Hamburger);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    在这里插入图片描述

    接口隔离原则

    建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

    这和“单一职责原则”类似,但它们还有一些不同:

    • 单一职责原则:一个类或函数只承担一个职责,所以同一个功能的所有实现函数可能会放到同一个类中。

    • 接口隔离原则:减少接口的粒度,每个类只需要实现自己需要的接口即可。

    所以单一职责原则和接口隔离原则可能会无法同时保证。

    迪米特法则

    尽量减少对象之间的交互,从而减小类之间的耦合。

    开闭原则

    当增加新功能,不应该通过修改已经存在的代码来进行,而是应该通过扩展代码,比如增加新类、增加新的成员函数 来实现。


    C++各种设计模式

    正在完善,后面再写。。。。

    模板方法模式

    C++设计模式—模板方法模式

    工厂模式

    C++设计模式----工厂模式
    C++设计模式—原型模式
    C++设计模式—建造者/构建器/构建者/生成器模式

    策略模式

    C++设计模式—策略模式

    观察者模式

    C++设计模式—观察者模式

    装饰器模式

    C++设计模式----装饰器模式

    单例模式

    C++ 特殊类的设计
    C++设计模式—单例模式

    外观模式

    C++设计模式—外观模式

  • 相关阅读:
    NOIP2023模拟3联测24-博弈树
    PaddleSeg(1)配置文件详解
    时间序列的栅格数据Sen+MK分析(R语言)源自徐洋——NDVI时间序列分析之Sen+MK分析全过程梳理
    Linux下的截图工具 —— Spectable
    2024目前三种有效加速国内Github
    apt 常用命令
    FPGA UDP RGMII 千兆以太网(1)
    ubuntu18.04编译GB28181Server
    亚马逊云科技与德勤中国推出新工具,有效缓解生成式AI时代下的安全问题
    敲了几万行源码后,我给Mybatis画了张“全地图”
  • 原文地址:https://blog.csdn.net/qq_52670477/article/details/126607671