设计模式之开闭原则
什么是开闭原则?
开放封闭原则称为OCP原则(Open Closed Principle)是所有面向对象原则的核心。
“开闭原则”是面向对象编程中最基础和最重要的设计原则之一。
软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一目标的最直接体现。其他的设计原则,很多时候是为实现这一目标服务的。
为什么要用开闭原则?
如果在进行功能扩展的时候,添加额外的类是没问题的,但因为功能扩展而修改之前运行正常的程序,这是忌讳的,不被允许的。因为一旦修改之前运行正常的程序,就会导致项目整体要进行全方位的重新测试。这是相当麻烦的过程。导致问题的主要原因是:代码和代码之间的耦合度太高。
先来看开闭原则的定义:
Software entities like classes,modules and functions should be open for extension but closed for modifications 一个软件实体, 如类, 模块, 函数等应该对扩展开放, 对修改封闭.
这也是开放封闭原则的核心思想:对扩展开放,对修改封闭.
这是什么含义呢?
- 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况,也就是指我们系统中的模块、类、方法对它们的提供者(开发者)应该是开放的,提供者可以对系统进行扩展(新增)新的功能。
- 对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对已有代码进行任何修改,也就是指系统中的模块、类、方法对它们的使用者(调用者)应该是关闭的。使用者使用这些功能时,不会因为提供方新增了功能而导致使用者(调用者)也进行相应修改。
如何实现开放封闭原则呢?
“需求总是变化”、“世界上没有一个软件是不变的”。这里投射出的意思是:需求总是变化的, 可是对于软件设计者来说, 如何才能做到不对原有系统修改的前提下, 实现灵活的扩展. 这就是开闭原则要实现的.
我们在设计系统的时候, 不可能设想一次性把需求确定后, 后面就不改变了.这不科学也不现实的. 既然需求是一定会变化的, 那么我们要如何优雅的面对这种变化呢? 如何设计可以使软件相对容易修改, 不至于需求一变, 就要把整个程序推到重来?
依赖与抽象
实现开放封闭的核心思想就是面对抽象编程,而不是面对具体编程,因为抽象相对稳定。 让类依赖于固定的抽象,所以对修改是封闭的;而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。这是实施开放封闭原则的基本思路。
如何落地开闭原则
如果当前的设计不符合开放封闭原则,则必须进行重构。常用的设计模式主要有模板方法(Template Method)设计模式和策略(Strategy)设计模式。而封装变化,是实现这一原则的重要手段,将经常发生变化的部分封装为一个类。
开闭原则的重要性
开闭原则是最基础的一个原则,其他五个原则都是它的具体形态。开闭原则是其精神领袖,其他五个原则是指导设计的工具和方法。
1. 开闭原则对测试的影响
单元测试通过一个方法一般需要三种方法:正常数据测试,边界数据测试,异常抛出测试,特别重要的可能需要十几个测试方法。如果修改一个实现方法,可能就要修改多个测试方法,这也可能会出现纰漏。所以需要通过扩展来实现业务需求变化。
2. 开闭原则可以提高复用性
在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑。如何提高复用性?缩小逻辑力度,知道一个逻辑不可在拆分为止。
3. 开闭原则可以提高可维护性
一款软件投产后,维护人员的工作不仅仅是对数据进行维护,还可能要对程序进行扩展,维护人员最乐意的是就是对一个类进行扩展,而不是修改一个类。
4. 面向对象开发的要求
万物皆对象,我们需要把所有的事物都抽象成对象,然后针对对象进行操作,但是万物皆运动,有运动就有变化,有变化就要有策略去应对变化。怎样快速应对呢?这就需要在设计之初考虑到所有可能变化的因素,然后留下接口,等待“可能”转变为“现实"。
如何使用开闭原则
1、抽象约束
抽象对一组事物的通用描述,没有具体的实现,也就表示它可以有非常多的可能性,可以跟随需求的变化而变化。因此,通过接口或抽象类可以约束一组可能变化的行为,并且能够实现对扩展开放。
2、元数据控件模块行为
3、制定项目章程
4、封装变化
将相同的变化封装到一个接口或抽象类中,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同变化出现在同一个接口或抽象类中。
注意事项
开闭原则对扩展开放,对修改关闭,并不意味这不做任何修改,低层模块的的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段。
变化可以归纳为3种:
1. 逻辑变化
只变化一个逻辑,而不改变其他模块。可以修改原有类中的方式来完成,前提条件是所有依赖或关联类都按照相同的逻辑处理。
2. 子模块变化
一个模块变化,必然会对其他的模块产生影响,特别是一个低层次的模块变化必然引起高层次模块的变化。因此,在通过扩展完成变化是,高层次的模块修改是必然的。
3. 可见视图变化
可见视图是提供给客户使用的界面,如JSP程序,Swing界面等,该部分的变化一般会引起连锁反应。最司空见惯的就是业务耦合变化。比如,按原有需求需要展示6列,然后突然有一天需要增加1列,而且这一列需要跨N张表,处理M个逻辑才能展现出来,这样的变化是比较恐怖的,但还是可以通过扩展来完成变化,这就需要看我们原有的设计是否灵活了。
案例1:Windows 的桌面主题设计。
分析:Windows 的主题是桌面背景图片、窗口颜色和声音等元素的组合。用户可以根据自己的喜爱更换自己的桌面主题,也可以从网上下载新的主题。这些主题有共同的特点,可以为其定义一个抽象类(Abstract Subject),而每个具体的主题(Specific Subject)是其子类。用户窗体可以根据需要选择或者增加新的主题,而不需要修改原代码,所以它是满足开闭原则的,其类图如图 所示。
案例2:比如现在国庆节,搞图书打折活动,有以下几种做法:
1.新增打折接口
2.修改原油NewBook类
3.新增打折书籍类
按照开放扩展关闭修改原则,应该是第三种方案,这样原有代码不会受到修改。
//书籍接口
public interface IBook {String getBookName();String getAuthor();int getPrice();
}
//书籍类
public class NewBook implements IBook {private String bookName;private String author;private int price; public NewBook(String bookName, String author, int price){this.bookName = bookName;this.author = author;this.price = price;}@Overridepublic String getBookName() {return this.bookName;}@Overridepublic String getAuthor() {return this.author;}@Overridepublic int getPrice() {return this.price;}
}
//打折书籍类
public class OffBook extends NewBook {public OffBook(String bookName, String author, int price) {super(bookName, author, price);}public Double getPrice2(){return this.getPrice()*0.7;}
}
相关文章:

设计模式之开闭原则
什么是开闭原则? 开放封闭原则称为OCP原则(Open Closed Principle)是所有面向对象原则的核心。 “开闭原则”是面向对象编程中最基础和最重要的设计原则之一。 软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一…...

Linux中的file命令:查看文件类型
2023年8月1日,周二上午 目录 简要说明使用方法MIME类型举例说明 简要说明 在Linux中,file命令用于识别文件类型。 file命令可以识别各种类型的文件,包括普通文件、目录、符号链接、设备文件、压缩文件、二进制可执行文件等。 它是一个非常…...

使用WiFi测量仪进行机器人定位的粒子过滤器研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

【vue】vue 里面使用 v-html 插入的文本带有换行符‘\n‘不换行
最近开发vue2 项目 ,接口返回的是类似于这样的数据:我是第一行的哦\n我是第二行的哦 我是直接这样渲染的, //html <p v-htmltext></p>//渲染值 this.text "我是第一行的哦\n我是第二行的哦"但结果却是不如意&#x…...
Java失效算法与应用(FIFO、LRU、LFU)
文章目录 一、什么是失效算法二、先来先淘汰(FIFO)1、FIFO概述2、Java实现FIFO3、FIFO特点 三、最久未用淘汰(LRU)1、LRU概述2、Java实现LRU 四、最近最少使用(LFU)1、LFU概述2、Java实现LFU 五、应用案例 …...

Go语音介绍
Go语言介绍 Go 即Golang,是Google公司2009年11月正式对外公开的一门编程语言。 Go是静态强类型语言,是区别于解析型语言的编译型语言。 解析型语言——源代码是先翻译为中间代码,然后由解析器对代码进行解释执行。 编译型语言——源代码编…...

Vue2与Vue3响应式原理
Vue2的响应式 Vue3的响应式...

flask中写一个基础的sqlHelper类
写一个SQLHelper类: from flask_sqlalchemy import SQLAlchemydb SQLAlchemy()class SQLHelper:staticmethoddef add(record):db.session.add(record)return SQLHelper.session_commit()staticmethoddef add_all(records):db.session.add_all(records)return SQLH…...

opencv的Mask操作,选择图片中感兴趣的区域
最近做目标检测任务的时候,需要对固定区域的内容进行检测,要用到opencv的mask操作,选择图片固定的区域 代码 import cv2 import numpy as npimg cv2.imread(data/images/smoking.png)# 弹出一个框 让你选择ROI | x,y是左上角的坐标 x,y,w,…...

一次有趣的Webshell分析经历
一次有趣的Webshell分析经历 1.拉取源代码2.解密后门代码3.分析webshell逻辑4.分析404的原因5.附:格式化后的php代码 1.拉取源代码 在对某目标做敏感目录收集时发现对方网站备份源代码在根目录下的 backup.tar.gz,遂下载,先使用D盾分析有没有…...

【NLP概念源和流】 05-引进LSTM网络(第 5/20 部分)
一、说明 在上一篇博客中,我们讨论了原版RNN架构,也讨论了它的局限性。梯度消失是一个非常重要的缺点,它限制了RNN对较短序列的建模。香草 RNN 在相关输入事件和目标信号之间存在超过 5-10 个离散时间步长的时间滞时无法学习。这基本上限制了香草RNN在许多实际问题上的应用,…...

Vue没有node_modules怎么办
npm install 一下 然后再npm run serve 就可以运行了...

企业级高负载web服务器-Tomcat小项目
目录 web静态动态页面区别安装java环境安装Tomcat安装Tomcat包到目录查看Tomcat主目录结构查看Tomcat配置目录结构Tomcat管理Tomcat web管理功能 部署jpress应用 web静态动态页面区别 静态页面: 在网站设计中,纯粹HTML格式的网页(可以包含图…...

《golang设计模式》第一部分·创建型模式-03-建造者模式(Builder)
文章目录 1. 概念1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概念 1.1 角色 Builder(抽象建造者):给出一个抽象接口,以规范产品对象的各个组成成分的建造。ConcreteBuilder(具体建造者)&a…...

git 忽略掉不需要的文件
第一步:创建.gitignore文件 touch .gitignore 第二步:使用vi编辑器 输入不需要的文件,或用通配符*来忽视一系列文件 效果:...

摄像机sd卡格式化怎么恢复数据?简单五步轻松解决
在使用摄像机时,有时不慎将SD卡格式化,导致重要的照片或视频文件丢失。然而,不必惊慌,本文将详细解释如何恢复被格式化的摄像机SD卡上的数据,可通过下面提供的五步,轻松解决数据丢失问题,以确保…...

1-4 AUTOSAR方法论--开发流程
目录 一、方法论 二、单个ECU开发流程 一、方法论 AUTOSAR 方法论(AUTOSAR Methodology)中车用控制器软件的开发涉及系统级、ECU 级的开发。 系统级:主要考虑系统功能需求、硬件资源、系统约束,然后建立系统架构; 输…...

Win10查询硬盘序列号
添加wmic命令 winR cmd命令 wmic diskdrive get model, serialnumber...

减少错误和重复工作:PDM系统的智能排错功能
减少错误和重复工作:PDM系统的智能排错功能 在产品开发和制造过程中,错误和重复工作常常是企业面临的挑战。这不仅浪费了宝贵的时间和资源,还可能导致产品质量下降和生产延误。PDM系统(Product Data Management,产品数…...
【面试题】作用域面试题
作用域 全局作用域局部作用域(函数里)也称函数作用域块级作用域 {}包裹的 例如if for 括号()也算 变量 全局变量 谁都能用,在函数内也可以局部变量,只能在该函数内用,如果这个函数嵌套了子函…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...