03.设计原则之开闭原则

开闭原则

开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则。 在23种设计模式中,大部分的设计模式都是为了解决代码的扩展性问题而存在,主要遵循的原则就是 开闭原则

定义

scss 复制代码
开闭原则(Open-Closed Principle, OCP):一个软件实体(模块、类、方法)应当对扩展开放,对修改关闭。
即软件实体应尽量在不修改原有代码的情况下进行扩展。即实现在不修改源代码的情况下改变这个
模块的行为

这个描述比较抽象,我们详细描述一下,那就是,添加一个新的功能应该是,在已有的代码基础上扩展 代码(新增模块、类、方法等)而非修改已有代码(修改模块、类、方法)

为了让你更好的理解这个原则,我举一个简单的例子进一步解释。

开闭原则实例

实例说明:某图形界面系统提供了各种不同形状的按钮,客户端代码可针对这些按钮编程,用户可能会改变要求 使用不同的按钮,原始代码如下: 界面窗口LoginForm代码

csharp 复制代码
// 该类组合了一个圆形按钮
public class LoginForm {
    private CircleBtn circleBtn;
    public void display() {
        circleBtn.view();
    }
}

圆形按钮CircleBtn代码

csharp 复制代码
public class CircleBtn {
    public void view() {
        System.out.println("i am a CircleBtn");
    }
}

矩形按钮RectBtn代码

csharp 复制代码
public class RectBtn {
    public void view() {
        System.out.println("i am a RectBtn");
    }
}

分析

我们知道设计原则就是一把尺子,可以衡量你设计方案的优劣, 那么我们就用开闭原则来丈量一下原始代码设计带来的问题 上述代码对扩展是开放的,如果你想增加一个按钮的话直接增加一个类就行了,如

csharp 复制代码
public class TriangleBtn {
    public void view() {
        System.out.println("i am a TriangleBtn");
    }
}

但是上述代码对修改没有关闭,因为LoginForm中依赖了具体的按钮子类,如果需要切换按钮 那么每次都要修改LoginForm中的代码。

重构(封装可变的实现,暴露不变的接口)

通过上面的问题,可知,按钮的切换显示,会导致LoginForm代码修改,那么不同按钮显示的需求就是 变化点。我们要封装这个变化点,封装变化点的手段就是面向抽象或者接口编程,让LoginForm依赖接口 就能做到按钮显示的变化,LoginForm的代码也不用跟着修改。

定义BtnInterface接口

csharp 复制代码
public interface BtnInterface {
    void view();
}

CircleBtn类

typescript 复制代码
public class CircleBtn implements BtnInterface{

    @Override
    public void view() {
        System.out.println("i am a CircleBtn");
    }

}

RectBtn类

typescript 复制代码
public class RectBtn implements BtnInterface{

    @Override
    public void view() {
        System.out.println("i am a RectBtn");
    }

}

LoginForm类

csharp 复制代码
// 此类依赖的接口,子类无论如何怎么变,LoginForm代码都不会变
public class LoginForm {
    private BtnInterface btnInterface;
    public void display() {
        btnInterface.view();
    }
}

讨论

讨论1. 修改代码就违反了开闭原则吗

不见得修改代码就违反了开闭原则,比如,我们系统中有一个用户管理模块,有一个user实体类,现在给这个实体类增加一个属性,算作修改还是 扩展? 开闭原则的定义:软件实体(模块、类、方法等)应该"对扩展开放、对修改关闭"。 从定义中,我们可以看出,开闭原则可以应用在不同粒度的代码中,可以是模块,也可以类,还可以是方法(及其属性)。 同样一个代码改动,在粗代码粒度下,被认定为"修改",在细代码粒度下,又可以被认定为"扩展"。 比如,改动一,添加属性和方法相当于修改类,在类这个层面, 这个代码改动可以被认定为"修改";但这个代码改动并没有修改已有的属性和方法,在方法(及其属性)这一层面,它又可以被认定为"扩展" 实际上,我们也没必要纠结某个代码改动是"修改"还是"扩展",更没必要太纠结它是否违反"开闭原则"。 我们回到这条原则的设计初衷:只要它没有破坏原有的代码的正常运行,没有破坏原有的单元测试,我们就可以说,这是一个合格的代码改动

总结

最常用的提高代码扩展性的方法有:

  1. 多态
  2. 依赖注入
  3. 基于接口编程
相关推荐
QQ_216962909636 分钟前
【项目编号:project95315】SpringBoot公共自习室管理系统:座位预约、房间管理、签到核销、公告规则完整实战
java·spring boot·后端
嵌入式阿蔡1 小时前
面试高频考点 01:volatile / 中断 / 堆栈 八股精讲
java·面试·职场和发展·嵌入式实时数据库
秋饼1 小时前
LangChain4j + Java 实现企业级 Text-to-SQL 智能问数系统:从自然语言到安全可控的数据洞察
java·ai·技术分享·后端开发
Escalating_xu1 小时前
【C++ STL简介】从六大组件到容器、迭代器与算法协作
java·c++·算法
何以解忧,唯有..2 小时前
Python os模块详解:文件与目录操作指南
java·服务器·python
Csxyzj2 小时前
kubernetes集群部署方法
java·linux·kubernetes
SQL-First布道者2 小时前
全面解构传统持久层框架,拥抱真正的 SQL-First
java·数据库·spring boot·sql·spring·mybatis·spring jdbc
小蒜学长2 小时前
vue旅游攻略网站(代码+数据库+LW)
java·数据库·vue.js·spring boot·后端·旅游
凤山老林2 小时前
轻量级规则引擎落地:Spring Boot 集成 LiteFlow 实现动态业务编排与热更新
java·spring boot·后端·规则引擎·liteflow
夏贰四3 小时前
管控数据加载调度如何规避任务冲突?数据加载运维监控体系该如何搭建?
java·大数据·运维