开放-封闭原则(The Open-Closed Principle)

2023-11-05

1 定义

       软件实体(类、模块、函数等等)应该是可以扩展的,但是不可修改的。开放-封闭原则主要体现在两个方面:

  • 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
  • 对修改封闭,意味着一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。

返回目录

2 如何实现

       在OCP中,实现开放封闭的方法就是抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以对修改就是封闭的;而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。这是实施开放-封闭原则的基本思路,同时这种机制建立在两个基本的设计原则的基础上,这就是Liskov替换原则和合成/聚合复用原则。
       实现OCP原则的设计模式主要有模板模式、策略模式。而封装变化,是实现这一原则的重要手段,将经常发生变化的状态封装为一个类。具体设计模式的编写可以参考如下文章(4.1,4.2章节):
https://blog.csdn.net/weixin_37624828/article/details/106059837

2.1 问题: 一个糟糕的设计

代码清单:

  1. ShapeType.java --枚举类
  2. Point.java
  3. Shape.java
  4. Square.java
  5. Circle.java
  6. OcpDemo1.java

1 ShapeType.java

public enum ShapeType {
    /**
     * 圆
     */
    CIRCLE,
    /**
     * 方
     */
    SQUARE
}

2 Point.java

public class Point {
    private double x;
    private double y;

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
    
    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

3 Shape.java

public abstract class Shape {
    ShapeType itsType;
}

4 Square.java

public class Square extends Shape {
   double itsSide;
   Point itsToLeft;

   public Square(ShapeType itsType, double itsSide, Point itsToLeft) {
       this.itsType = itsType;
       this.itsSide = itsSide;
       this.itsToLeft = itsToLeft;
   }

   public void drawSquare(){
       System.out.println("绘制方形");
       System.out.println(toString());
   }

   @Override
   public String toString() {
       return "Square{" +
               "itsSide=" + itsSide +
               ", itsToLeft=" + itsToLeft +
               ", itsType=" + itsType +
               '}';
   }
}

5 Circle.java

public class Circle extends Shape{
    double itsRadius;
    Point itsCenter;

    public Circle(ShapeType itsType, double itsRadius, Point itsCenter) {
        this.itsType = itsType;
        this.itsRadius = itsRadius;
        this.itsCenter = itsCenter;
    }

    public void drawCircle(){
        System.out.println("绘制圆形");
        System.out.println(toString());
    }

    @Override
    public String toString() {
        return "Circle{" +
                "itsRadius=" + itsRadius +
                ", itsCenter=" + itsCenter +
                ", itsType=" + itsType +
                '}';
    }
}

6 OcpDemo1.java

public class OcpDemo1 {
    public static void main(String[] args) {
        List<Shape> shapeList = new ArrayList<>();
        shapeList.add(new Circle(ShapeType.CIRCLE, 1, new Point(1, 2)));
        shapeList.add(new Square(ShapeType.SQUARE, 1, new Point(1, 2)));
        shapeList.add(new Square(ShapeType.SQUARE, 1, new Point(1, 2)));
        shapeList.add(new Circle(ShapeType.CIRCLE, 1, new Point(1, 2)));

        for (int i = 0; i < shapeList.size(); i++) {
            switch (shapeList.get(i).itsType){
                case CIRCLE:
                    ((Circle) shapeList.get(i)).drawCircle();
                    break;
                case SQUARE:
                    ((Square) shapeList.get(i)).drawSquare();
                    break;
                default:
                    System.out.println("非圆非方");
            }
        }
    }
}

       OcpDemo1中的main方法就不符合OCP原则,因为它对新的类型的添加不是封闭的,每次添加一个新类型就需要在switch菜单中添加一个选项。
       在实际工作中并非每一个判断条件都像上述选项中这么规范,一般的判断条件都是由逻辑操作符组合而成的复杂判断条件,因此这样的设计是一个糟糕的设计。

2.2 解决方案:遵循OCP

类图

在这里插入图片描述注:蓝色代表Shape类型是可以扩展的

代码清单:

  1. Shape.java --接口
  2. Circle.java
  3. Square.java
  4. OcpDemo2.java

1 Shape.java

public interface Shape {
    /**
     * 绘制形状方法
     */
    void draw();
}

2 Circle.java

public class Circle implements Shape{
    ShapeType itsType;
    double itsRadius;
    Point itsCenter;

    Circle(ShapeType itsType, double itsRadius, Point itsCenter) {
        this.itsType = itsType;
        this.itsRadius = itsRadius;
        this.itsCenter = itsCenter;
    }

    @Override
    public void draw(){
        System.out.println("绘制圆形");
        System.out.println(toString());
    }

    @Override
    public String toString() {
        return "Circle{" +
                "itsRadius=" + itsRadius +
                ", itsCenter=" + itsCenter +
                ", itsType=" + itsType +
                '}';
    }
}

3 Square.java

public class Square implements Shape {
    ShapeType itsType;
    double itsSide;
    Point itsToLeft;

    public Square(ShapeType itsType, double itsSide, Point itsToLeft) {
        this.itsType = itsType;
        this.itsSide = itsSide;
        this.itsToLeft = itsToLeft;
    }

    @Override
    public void draw(){
        System.out.println("绘制方形");
        System.out.println(toString());
    }

    @Override
    public String toString() {
        return "Square{" +
                "itsSide=" + itsSide +
                ", itsToLeft=" + itsToLeft +
                ", itsType=" + itsType +
                '}';
    }
}

4 OcpDemo2.java

public class OcpDemo2 {
    public static void main(String[] args) {
        List<Shape> shapeList = new ArrayList<>();
        shapeList.add(new Circle(ShapeType.CIRCLE, 1, new Point(1, 2)));
        shapeList.add(new Square(ShapeType.SQUARE, 1, new Point(1, 2)));
        shapeList.add(new Square(ShapeType.SQUARE, 1, new Point(1, 2)));
        shapeList.add(new Circle(ShapeType.CIRCLE, 1, new Point(1, 2)));
        // 绘制图形
        drawShape(shapeList);
    }

    /**
     * 绘制图形
     * @param list 图形集合
     */
    private static void drawShape(List<Shape> list) {
        for (Shape shape : list) {
            shape.draw();
        }
    }
}

       如果list需要添加一个新类型时,drawShape方法无需做出修改,只需要实现一个新类型即可,这就遵循了开放-封闭原则。Shape类型是可以扩展的(遵循了开放),drawShape方法无需修改(遵循了封闭)。

3 结论

       在许多方面,OCP都是面向对象设计的核心所在。遵循这个原则可以体现面向对象技术的好处(灵活性、可重用性以及可维护性)。但是并不是只要使用面向对象语言就需要遵循这个原则,开发人员应该对程序中频繁变化的部分做出抽象,而不是对程序肆意抽象。抽象和拒绝抽象同样重要

返回目录

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

开放-封闭原则(The Open-Closed Principle) 的相关文章

  • JNA - EnumProcessModules() 未返回所有 DLL?

    我试图从游戏中读取坐标 当我在通过 OpenProcess 接收的 HANDLE 上使用 ReadProcessMemory 以及我在 CheatEngine 中找到的内存时 效果非常好 例如 如果我知道正在运行的进程中的浮点值是0x5AB
  • RxJava + Retrofit 2 的正确使用方法

    我有这样的 JSON success true data id 29 name u0420 u0435 u0441 u0442 u043e u0440 u0430 u0446 u0456 u044f u0411 u0430 u0447 u0
  • 如何将抽象工厂与单例模式结合起来? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在用 java 编码 并且对这些模式很陌生 谁能给我一个也使用单例的工厂抽象的例子 这是一个实现类的示例单例模式 这个实现也是线程安全
  • 初始堆大小无效。无法创建Java虚拟机

    我遇到了下一个问题 我尝试通过startup bat手动启动Tomcat 但似乎没有显示任何结果 然后我尝试运行shutdown bat 控制台显示如下 D apache tomcat 7 0 35 bin gt startup bat U
  • EL 通过 Scriptlet

    在 JSP 中使用 EL 相对于 scriptlet 的优势是什么 EL 被认为是无脚本语言 EL 使 JSP 免受容易出错原始 Java 代码并强制您根据 MVC 思想编写 JSP EL 或像 JSTL 这样的标签库 不可能实现的任何事情
  • 从字符串生成密钥?

    我需要从字符串生成一个密钥 以便我始终可以从同一字符串创建相同的密钥 具体来说是一个Key对象 这样我就可以用它来创建Cipher进而创建SealedObject 这在 Java 中可行吗 我应该考虑什么类 方法组合才能做到这一点 对于 A
  • c和java语言中的换行符

    现在行分隔符取决于系统 但在 C 程序中我使用 n 作为行分隔符 无论我在 Windows 还是 Linux 中运行它都可以正常工作 为什么 在java中 我们必须使用 n 因为它与系统相关 那么为什么我们在c中使用 n 作为新行 而不管我
  • 如何在 Java 中使用 StringUtils?

    我是 Java 初学者 我想用StringUtils replace但 Eclipse 输出 StringUtils cannot be resolved I tried import java lang 但它不起作用 java lang不
  • 我需要一个字数统计程序[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我需要弄清
  • 在java中将DataURL图像转换为图像文件

    我在我的 java servlet 中接收图像 DataURL 它看起来像 data image jpeg base64 9j 4AAQSkZJRgABAQAAAQABAA 我需要将其另存为图像文件 我该怎么做 The simplest w
  • Java 中的 MP4 容器编写器

    我想找到一个免费的 Java MP4 容器 编写器 我不需要编码器 只需要能够根据预期值写入正确原子的编码器 Bonus对于这样一个库 也可以编写 有效 F4V 我更喜欢纯 Java 解决方案 而不是使用 JNI 或外部可执行文件的解决方案
  • 如何在Java中打印保留2位小数的浮点数?

    我可以用System out print 您可以使用printf http java sun com j2se 1 5 0 docs api java io PrintStream html printf 28java lang Strin
  • 为什么我要使用责任链而不是 switch 语句

    考虑一下您已经获得了多次验证 仅当要检查的对象属于某种类型时 这些验证才应生效 为什么我要使用责任链而不是 switch 语句 责任链示例 public class Executor Inject private ValidatorFact
  • 无法自动装配 org.springframework.mail.javamail.JavaMailSender

    尝试运行我的应用程序时遇到以下问题 所有的东西都调试过了 还是一无所获 IDE 毫无问题地找到了 bean 所以我对这里发生的情况感到非常困惑 SEVERE Exception sending context initialized eve
  • Java 不可变对象 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在学习不变性的概念 据我了解 一旦创建对象 不可变对象就无法更改其值 但我不明白不可变对象的以下用途 They are 自动是线程
  • Java 验证日期为 yyyyMMddHHmmss

    我想在java中验证给定的日期格式为yyyyMMddHHmmss 状况 应符合格式 yyyyMMddHHmmss 它应该验证当前日期 它应该验证与当前小时有 3 小时或 3 小时差异的小时数 如果满足所有三个条件 Java 方法应返回 tr
  • while 之后无法访问的语句[重复]

    这个问题在这里已经有答案了 我只是修改代码 在以下代码中出现错误 int x 1 System out println x x while true x System out println x x 错误在最后一行 我可以知道错误 错误 无
  • Spring Transactional 减慢了整个过程

    我正在尝试分析我有两堂课的情况 其中一个类是 ProcessImpl 它是起点并在内部调用其他子事务 我不知道出了什么问题 processImpl正在导入一些东西并将相关数据写入数据库 Specs Spring orm版本 3 2 18 发
  • 如何列出Resources文件夹中的所有文件(java/scala)

    我正在编写一个函数 需要访问资源中的文件夹 并循环遍历所有文件名 如果这些文件符合条件 则加载这些文件 new File getClass getResource images sprites getPath listFiles 返回空指针
  • Java 可变 BigInteger 类

    我正在使用 BigIntegers 进行计算 该计算使用一个调用 multiply 大约 1000 亿次的循环 并且从 BigInteger 创建新对象使其非常慢 我希望有人编写或找到了 MutableBigInteger 类 我在 jav

随机推荐

  • jdk源码调试显示变量

    原文地址 http my oschina net xionghui blog 497361 Java是一门开源的程序设计语言 喜欢研究源码的java开发者总会忍不住debug一下jdk源码 虽然官方的jdk自带了源码包src zip 然而在
  • LeetCode 面试题01.01. 判定字符是否唯一的两种解法

    本文唯一重点 按位取与的运算优先级比较低 至少比 和 都低 注意加括号 题目概述 题解 一 哈希表 思路是简单的 用第一个下标做字符 第二个下标做字符出现的次数 先遍历一遍字符串 把次数都统计好 然后再遍历一遍字符串 如果查询到某个字符的c
  • 解决中文乱码问起

    Java对数据库进行CRUD操作出现乱码 先查看web xml有没有配置字符编码过滤器
  • es6把多个class方法合并在一起

    前言 es6新增的class方法 现在想把他们多个合并到一起 最终生成一个新方法出来 思路 我们新建3个文件 分别为index js login js main js login js 和 main js是两个 class函数 将他们合并到
  • Huggingface-4.8.2自定义训练

    Huggingface走到4 8 2这个版本 已经有了很好的封装 训练一个语言网络只需要调用Trainer train 即可完成 如果要根据自己的需求修改训练的过程 比如自定义loss 输出梯度 直接修改huggingface的源码显然是不
  • Spire.Doc系列教程:C# Word 图片替换

    Spire Doc支持在Word文档中对图片进行多种操作 如添加 提取 替换和删除等 本文主要介绍如何使用Spire Doc替换Word文档中的图片 在Spire Doc中 图片替换包含文本替换图片和图片替换图片两种方式 以下部分将对这两种
  • js中file、blob、base64的相互转换,前端图片压缩、裁剪、文件格式转换

    最近做了个前端图片压缩 用到了很多js文件操作api 整理一下防止健忘 1 file转base64 获取文件的Base64 param file File 文件 param callback Function 回调函数 参数为获取到的bas
  • (个人)AR电子书系统创新实训中期总结

    AR电子书项目实训中期总结 已完成内容 使用ZXing Net库 验证并测试Unity中生成和识别二维码的功能 使用Unity自带的WWW类测试并实现了本地图像和视屏资源的读取 并使用File方法实现了资源的写入 通过www读入本地文件 p
  • Vue项目运行时报错These relative modules were not found

    Vue项目运行时报错These relative modules were not found ERROR Failed to compile with 2 errors These relative modules were not fo
  • 用git,clone依赖的库

    git clone https github com influxdata influxdb java git cd crfasrnn git submodule update init recursive 或 git clone recu
  • 汇编程序debug成功但不能直接运行

    王爽 汇编语言 实验10中的dtoc子程序 运行不了 但是单步调试可以通过并且结果正常 我的代码如下 assume cs code data segment db 10 dup 0 data ends code segment start
  • 标注转换与编辑软件

    标注转换与编辑软件 https github com Fafa DL LabelConvert 1 支持XML转JSON XML转TXT TXT转XML JSON转XML 2 XML JSON类别替换 3 XML JSON类别删除 该Git
  • 《软件测试的艺术》第3章:代码检查、走查与评审

    软件测试的艺术 软件开发人员通常不会考虑到的一种测试形式 人工测试 大多数人认为 因为程序是为了供机器执行而编写的 那么也应由机器来对程序进行测试 这种想法是有问题的 人工测试方法在暴露错误方面是很有成效的 实际上 大多数的软件项目都应使用
  • 100天精通Python(数据分析篇)——第75天:Pandas数据预处理之数据标准化

    文章目录 专栏导读 1 数据标准化是什么 2 数据标准化的作用 3 数据标准化的方法 4 离差标准化 5 标准差标准化 6 小数定标标准化 书籍介绍 专栏导读
  • Python 模块中的变量

    模块 module 其实就是py文件 里面定义了一些函数 类 变量等 包 package 是多个模块的聚合体形成的文件夹 里面可以是多个py文件 也可以嵌套文件夹 库是参考其他编程语言的说法 是指完成一定功能的代码集合 在python中的形
  • 第十四届蓝桥杯模拟赛第一期试题【Java解析】

    目录 A 二进制位数 问题描述 答案提交 参考答案 解析 B 晨跑 问题描述 答案提交 参考答案 解析 C 调和级数 问题描述 答案提交 参考答案 解析 D 山谷 问题描述 答案提交 参考答案 解析 E 最小矩阵 问题描述 答案提交 答案
  • css 栅格布局el-row(xs,sm,md,lg,xl)

    xs 12 24 12 2 所以当屏幕尺寸 lt 768px时 手机 每行展示2个div class com item sm 8 24 8 3 所以当屏幕尺寸 gt 768px时 平板 每行展示3个div class com item md
  • spark.ml支持算法总结

    文章目录 spark支持算法总结 推荐模型 协同过滤 分类模型 1 逻辑回归 2 决策树 3 朴素贝叶斯 4 随机森林 5 梯度提升决策树回归 6 多层感知器分类 7 一对多分类 8 支持向量机 回归模型 1 线性回归 2 广义线性模型 3
  • F1值(F-Measure)、准确率(Precision)、召回率(Recall)简单理解

    准确率与召回率 Precision Recall 准确率和召回率是广泛用于信息检索和统计学分类领域的两个度量值 用来评价结果的质量 准确率 Precision Precision是检测出某类特征的数量与检测出的所有特征数量之间的比率 衡量的
  • 开放-封闭原则(The Open-Closed Principle)

    1 定义 软件实体 类 模块 函数等等 应该是可以扩展的 但是不可修改的 开放 封闭原则主要体现在两个方面 对扩展开放 意味着有新的需求或变化时 可以对现有代码进行扩展 以适应新的情况 对修改封闭 意味着一旦设计完成 就可以独立完成其工作