• 入门设计原则C++实现二:开闭原则


    软件实体应当对扩展开放,对修改关闭

    • Software entities should be open for extension,but closed for modification

    含义:

    • 对于扩展是开放的(Open for extension)。也就是模块的行为是可以扩展的,当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。

    • 对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。

    当项目需求变动时,在不修改源代码前提下,通过增加新类/新方法/新模块等方式满足新的需求,而不是修改。

    作用(优点)

    • 最基础的设计原则,前面讲的6个设计原则,跟后面讲设计模式目的都是为让程序(架构)能遵循开闭原则,或者说其他的原则都是开闭原则的具体形态。

    • 开闭原则是面向对象程序设计的终极目标,要求程序(架构)既要拥有一定的适应性和灵活性,又要具备稳定性和延续性

    • 方便测试,遵循开闭原则的程序模块只需要独立测试拓展模块即可

    • 提高复用性,符合开闭原则的组件设计,一般都是高内聚低耦合的设计

    实现方式

    开闭原则的实现基本围绕着抽象约束,封装变化这个思想展开的。即通过接口/抽象类来定义一个统一的规范,对功能做约束,而需要变动的因素或个体差异通过具体实现类/子类体现。

    案例分析

    一个饲养员喂羊动物的小案例

    示例1:

    #include 
    
    class Dog{
    public:
        void eat() {
            std::cout << "狗吃骨头" << std::endl;
        }
    };
    
    class Cat{
    public:
        void eat() {
            std::cout << "猫吃鱼" << std::endl;
        }
    };
    
    class Keeper{
    public:
        void feedDog(Dog* dog) {
            dog->eat();
        }
        void feedCat(Cat* cat) {
            cat->eat();
        }
    };
    
    int main(){
        Cat* cat = new Cat;
        Dog* dog = new Dog;
        Keeper* keeper = new Keeper;
    
        keeper->feedDog(dog);
        keeper->feedCat(cat);
    
        return 1;
    }
    
    
    • 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

    分析:这是一个很简答的小例子,一个猫类一个狗类,一个饲养员类,猫和狗具有吃的方法,饲养员具有喂猫和喂狗两种方法,main中调用也符合预期。这是现有的功能(旧功能),想象一下,当动物园新进一种动物,比如小鱼,代码应当如何变化呢

    1. 新增小鱼类,也具有吃的方法;新增类,属于模块级变化,设计模式中允许对拓展开放;

    2. 修改keeper类,增加喂小鱼的方法;keeper类属于旧的功能类,但是随着新需求的增加要被修改,违背了原则中的对修改关闭;

    3. main中使用。

    同时,该例子也违背了

    高层次的模块不要依赖低层次的模块,二者都应该依赖于其抽象(案例中 keeper和dog都是具体类,keeper依赖dog类,因为dog类变化后,keeper可能会出问题)

    抽象不应该依赖于具体,而是具体应该依赖于抽象(具体类依赖了具体类,而不是依赖抽象类)

    示例2

    #include 
    
    class Animal{
    public:
        virtual void eat() = 0; //纯虚函数
    };
    
    class Dog : public Animal{
    public:
        virtual void eat() override {
            std::cout << "狗吃骨头" << std::endl;
        }
    };
    
    class Cat : public Animal{
    public:
        virtual void eat() override {
            std::cout << "猫吃鱼" << std::endl;
        }
    };
    
    class Keeper{
    public:
        void feedAnimal(Animal* a) {
            a->eat();
        }
    };
    
    int main(){
        Animal* cat = new Cat;
        Animal* dog = new Dog;
        Keeper* keeper = new Keeper;
    
        keeper->feedAnimal(dog);
        keeper->feedAnimal(cat);
    
        return 1;
    }
    
    
    • 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

    分析:从狗类和猫类中抽象出来动物类

  • 相关阅读:
    CSS基础选择器,CSS字体属性,CSS文本属性
    数据驾驶舱只是面子工程?它的真正作用你根本就不了解
    ros入门之一,发布者,订阅者的python文件创建以及最后的终端执行(含python文件代码)
    JAVA仓库管理系统(附源码+调试)
    基于go标准分层架构项目设计实现
    Hyperinteger
    VMware下linux中ping报错unknown host的解决办法
    OpenCV项目开发实战--forEach的并行像素访问与其它方法的性能比较
    C++面试八股文:知道std::unordered_set/std::unordered_map吗?
    day02-2
  • 原文地址:https://blog.csdn.net/u011852872/article/details/126134090