当前位置: 首页 > news >正文

设计模式-开放封闭原则

开放封闭原则

什么是开放封闭原则?

开放封闭原则是 SOLID 原则中的第二个字母 "O",由伯特兰·迈耶 (Bertrand Meyer) 在其著作《面向对象软件构造》中提出。它的核心思想是:

软件实体(类、模块、函数等)应该对于扩展是开放的,对于修改是封闭的。 (Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.)

这句话听起来有点矛盾,我们来拆解一下:

  • 对于扩展是开放的 (Open for extension): 这意味着当软件需要增加新的功能或行为时,我们应该能够通过添加新的代码来实现,而不是修改已有的、经过测试的代码。

  • 对于修改是封闭的 (Closed for modification): 这意味着一旦一个模块或类的核心功能开发完成并通过测试,我们应该尽量避免去修改它已有的代码。因为修改已有的代码可能会引入新的 bug,影响到依赖这个模块的其他部分。

为什么这个原则很重要?

遵守开放封闭原则可以带来以下显著的好处:

  1. 提高系统的稳定性和可靠性: 通过不修改已有的、稳定的代码,可以减少引入新错误的风险。新的功能通过新的代码实现,即使新代码有问题,影响范围也相对可控。

  2. 增强系统的可维护性: 当需要增加新功能时,开发人员不需要去理解和修改复杂的旧代码,只需要关注如何编写新的扩展代码,降低了维护成本。

  3. 提高系统的可复用性: 设计良好的、对修改封闭的模块更容易被其他系统或项目复用。

  4. 促进系统的灵活性和可扩展性: 系统能够更容易地适应需求的变化,因为添加新功能就像“插拔”组件一样。

  5. 降低回归测试的成本: 由于核心代码未被修改,回归测试的范围可以更集中在新添加的扩展部分。

如何实现开放封闭原则?

实现开放封闭原则的关键在于抽象化多态。通常可以通过以下方式来实现:

  1. 使用抽象类和接口:

    • 定义稳定的抽象层(接口或抽象类),封装变化的部分。

    • 具体的实现类继承抽象类或实现接口,从而实现扩展。

    • 客户端代码依赖于抽象层,而不是具体的实现类。

  2. 使用参数化行为(例如策略模式、模板方法模式):

    • 将可变的行为抽象成策略或模板中的可变步骤,允许通过传入不同的参数或实现不同的子类来改变行为。

  3. 使用钩子方法 (Hook Methods) 或回调机制:

    • 在稳定的框架代码中预留“钩子”,允许通过实现这些钩子来扩展功能。

  4. 依赖注入 (Dependency Injection) 和控制反转 (Inversion of Control):

    • 通过将依赖关系从代码内部移到外部配置或容器管理,使得在不修改原有代码的情况下替换或增加依赖成为可能。

举个例子:

假设我们有一个图形编辑器,需要绘制不同的形状(如圆形、矩形)。

不好的设计 (违反 OCP):

// 反例:违反开放封闭原则
class GraphicEditor {public void drawShape(Object shape) {if (shape instanceof Circle) {drawCircle((Circle) shape);} else if (shape instanceof Rectangle) {drawRectangle((Rectangle) shape);}// 当需要增加新的形状(如三角形)时,必须修改这里的 if-else 结构// else if (shape instanceof Triangle) {//     drawTriangle((Triangle) shape);// }}
​private void drawCircle(Circle c) {System.out.println("Drawing a Circle");}
​private void drawRectangle(Rectangle r) {System.out.println("Drawing a Rectangle");}// private void drawTriangle(Triangle t) { ... }
}
​
class Circle { /* ... */ }
class Rectangle { /* ... */ }
// class Triangle { /* ... */ }

在这个例子中,GraphicEditor 类直接依赖于具体的形状类。每当需要支持一种新的形状时,都必须修改 drawShape 方法中的 if-else 逻辑。这违反了“对修改封闭”的原则。

好的设计 (遵循 OCP):

// 改进:遵循开放封闭原则
​
// 1. 定义抽象形状 (Shape) - 抽象层
interface Shape {void draw();
}
​
// 2. 具体形状实现抽象 (Concrete Shapes)
class Circle implements Shape {@Overridepublic void draw() {System.out.println("Drawing a Circle");}
}
​
class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("Drawing a Rectangle");}
}
​
// 3. 图形编辑器依赖于抽象 (GraphicEditor)
class GraphicEditor {// 依赖于抽象 Shape 接口public void drawShape(Shape shape) {shape.draw(); // 调用抽象方法,具体行为由传入的 Shape 对象决定}
}
​
// 当需要增加新的形状时,例如三角形:
class Triangle implements Shape {@Overridepublic void draw() {System.out.println("Drawing a Triangle");}
}
​
// 客户端使用
public class Client {public static void main(String[] args) {GraphicEditor editor = new GraphicEditor();
​Shape circle = new Circle();Shape rectangle = new Rectangle();Shape triangle = new Triangle(); // 新增的形状
​editor.drawShape(circle);    // 输出: Drawing a Circleeditor.drawShape(rectangle); // 输出: Drawing a Rectangleeditor.drawShape(triangle);  // 输出: Drawing a Triangle  <-- 无需修改 GraphicEditor 类
​}
}

在这个改进的设计中:

  • 我们定义了一个 Shape 接口作为抽象层。

  • Circle 和 Rectangle 是 Shape 接口的具体实现。

  • GraphicEditor 的 drawShape 方法接收一个 Shape 类型的参数,并调用其 draw() 方法。它不关心具体的形状是什么。

现在,如果我们需要增加一个新的形状,比如 Triangle:

  1. 我们只需要创建一个新的 Triangle 类并实现 Shape 接口。

  2. GraphicEditor 类的代码完全不需要修改。 它对于新的形状是“开放”的(可以通过添加新的 Shape 实现来扩展),对于已有的绘制逻辑是“封闭”的。

这就是开放封闭原则的威力。

总结:

开放封闭原则是面向对象设计中一个非常核心且重要的原则。它的目标是通过抽象来构建一个稳定的、不易被修改的核心系统,同时又能灵活地通过添加新的代码来扩展系统的功能。实现 OCP 的关键在于识别系统中可能变化的部分,并将这些变化封装在抽象之后,使得系统的其他部分依赖于这个稳定的抽象。

虽然在实际开发中,完全做到“对修改封闭”有时比较困难,甚至在某些情况下,适度的修改是必要的。但开放封闭原则提供了一个重要的设计目标和方向,引导我们编写出更健壮、更灵活、更易于维护的软件系统。

相关文章:

  • AI智能体策略FunctionCalling和ReAct有什么区别?
  • Qtc++开发遇到的问题-按钮点击不管用?
  • 大模型三大缺陷与RAG破解之道
  • 【leetcode】977. 有序数组的平方
  • ComfyUI 文生图,绘图要求中 正向提示词、负向提示词 有什么区别,webp又是什么格式 comfyui 那么喜欢它
  • 导出rpm包的方法
  • 【监控】Prometheus中的告警机制介绍
  • 与 PyCharm 官方沟通解决开发环境问题记录(进展:官方已推出2个新的修复版本)
  • 打造自己的开源组件:如何将 Starter 发布到 Maven Central?
  • 经典查找算法合集(上)
  • 59、【OS】【Nuttx】编码规范解读(七)
  • 【MQTT】TLS证书双向验证
  • ROS2 robot控制学习(一)
  • Java 并发编程通关秘籍——08死锁
  • STL-Library-Containers
  • Acrobat Reader 无法在 Windows 11及10 中打开的5种修复方法
  • 岛津Sonialvision X-ray X射线高压发生器控制台
  • RuoYi前后端分离框架集成Jasypt实现配置信息加密
  • C语言创意编程:用趣味实例玩转基础语法(2)
  • Redis工作原理解析
  • 电商网站取名/seo关键词排名优化销售
  • 自己建设网站麻烦吗/公司网站建设要多少钱
  • 网站由哪些部分组成部分组成部分/个人网站该怎么打广告
  • 网站建设与管理多选题/搜索网页内容
  • 注册网站后怎么建设/最新新闻热点事件
  • 哪个网站建设最好/网络推广推广培训