设计模式七大原则---开闭原则

一、概念

开闭原则(Open-Closed Principle,
OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
在开闭原则的定义中,软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类。

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

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

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

任何软件都需要面临一个很重要的问题,即它们的需求会随时间的推移而发生变化。当软件系统需要面对新的需求时,我们应该尽量保证系统的设计框架是稳定的。如果一个软件设计符合开闭原则,那么可以非常方便地对系统进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。随着软件规模越来越大,软件寿命越来越长,软件维护成本越来越高,设计满足开闭原则的软件系统也变得越来越重要。

为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在Java、C#等编程语言中,可以为系统定义一个相对稳定的抽象层,而将不同的实现行为移至具体的实现层中完成。在很多面向对象编程语言中都提供了接口、抽象类等机制,可以通过它们定义系统的抽象层,再通过具体类来进行扩展。如果需要修改系统的行为,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。

————————————————

二、例程

在这里插入图片描述

......
if (type.equals("pie")) {
PieChart chart = new PieChart();
chart.display();
}
else if (type.equals("bar")) {
BarChart chart = new BarChart();
chart.display();
}

在该代码中,如果需要增加一个新的图表类,如折线图LineChart,则需要修改ChartDisplay类的display()方法的源代码,增加新的判断逻辑,违反了开闭原则。

  现对该系统进行重构,使之符合开闭原则。

在本实例中,由于在ChartDisplay类的display()方法中针对每一个图表类编程,因此增加新的图表类不得不修改源代码。可以通过抽象化的方式对系统进行重构,使之增加新的图表类时无须修改源代码,满足开闭原则。具体做法如下:

  (1) 增加一个抽象图表类AbstractChart,将各种具体图表类作为其子类;

  (2)  ChartDisplay类针对抽象图表类进行编程,由客户端来决定使用哪种具体图表。

在这里插入图片描述

在图2中,我们引入了抽象图表类AbstractChart,且ChartDisplay针对抽象图表类进行编程,并通过setChart()方法由客户端来设置实例化的具体图表对象,在ChartDisplay的display()方法中调用chart对象的display()方法显示图表。如果需要增加一种新的图表,如折线图LineChart,只需要将LineChart也作为AbstractChart的子类,在客户端向ChartDisplay中注入一个LineChart对象即可,无须修改现有类库的源代码。

   注意:因为xml和properties等格式的配置文件是纯文本文件,可以直接通过VI编辑器或记事本进行编辑,且无须编译,因此在软件开发中,一般不把对配置文件的修改认为是对系统源代码的修改。如果一个系统在扩展时只涉及到修改配置文件,而原有的Java代码或C#代码没有做任何修改,该系统即可认为是一个符合开闭原则的系统。

三、代码示例

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

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

#include <iostream>

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;
}

————————————————
版权声明:本文为CSDN博主「龙俊杰的读书笔记」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u011852872/article/details/126134090

版权声明:本文为CSDN博主「LoveLion」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lovelion/article/details/7537584

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值