设计模式学习之开闭原则

学习内容均来自抖音号 【it楠老师教java】课程。

1、原理概述

开闭原则的英文全称是 Open Closed Principle,简写为 OCP。它的英文描述是:software entities (modules, classes, functions, etc.) should be open for extension , but closed for modification。我们把它翻译成中文就是:软件实体(模块、类、方法等)应该“对扩展开放、对修改关闭”。说人话就是,当我们需要添加一个新的功能时,应该在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。以下是一个常见的生产环境中的例子,我们将展示一个简化的电商平台的订单折扣策略。
你觉得下边的代码有问题吗?

class Order {
private double totalAmount ;
public Order ( double totalAmount ) {
this . totalAmount = totalAmount ;
}
// 计算折扣后的金额
public double getDiscountedAmount ( String discountType ) {
double discountedAmount = totalAmount ;
if ( discountType . equals ( "FESTIVAL" )) {
discountedAmount = totalAmount * 0.9 ; // 节日折扣, 9 }
else if ( discountType . equals ( "SEASONAL" )) {
discountedAmount = totalAmount * 0.8 ; // 季节折扣, 8
}
return discountedAmount ;
}
}

上述代码中, Order 类包含一个计算折扣金额的方法,它根据不同的折扣类型应用 折扣。当我们需要添加新的折扣类型时,就不得不需要修改 getDiscountedAmount 方法的代码,这显然是不合理的,这就违反了开闭原则。

遵循开闭原则的代码: 

// 抽象折扣策略接口
interface DiscountStrategy {
double getDiscountedAmount ( double totalAmount );
}
// 节日折扣策略
class FestivalDiscountStrategy implements DiscountStrategy {
@Override
public double getDiscountedAmount ( double totalAmount ) {
return totalAmount * 0.9 ; // 9
}
}
// 季节折扣策略
class SeasonalDiscountStrategy implements DiscountStrategy {
@Override
public double getDiscountedAmount ( double totalAmount ) {
return totalAmount * 0.8 ; // 8
}
}
class Order {
private double totalAmount ;
private DiscountStrategy discountStrategy ;
public Order ( double totalAmount , DiscountStrategy discountStrategy ) {
this . totalAmount = totalAmount ;
this . discountStrategy = discountStrategy ; }

在遵循开闭原则的代码中,我们定义了一个抽象的折扣策略接口 DiscountStrategy ,然后为每种折扣类型创建了一个实现该接口的策略类。 Order 类使用组合的方式,包含一个 DiscountStrategy 类型的成员变量,以便 在运行时设置或更改折扣策略,(可以通过编码,配置、依赖注入等形式)。这样, 当我们需要添加新的折扣类型时,只需实现 DiscountStrategy 接口即可,而无需 修改现有的 Order 代码。这个例子遵循了开闭原则。

这里相信很多初级程序员看到都会有种恍然大悟的感觉,或者某些中级程序员不注重代码风格的人也会有所顿悟,csdn上写设计模式的人有一大堆,但是没人告诉你怎么用,这个写的确实不错,都是写程序的人了,十几二十块真不算什么,这个老师还是有点水平的。

2、修改代码就意味着违背开闭原则吗

开闭原则的核心思想是要尽量减少对现有代码的修改,以降低修改带来的风险和影 响。在实际开发过程中,完全不修改代码是不现实的。当需求变更或者发现代码中的 错误时,修改代码是正常的。然而,开闭原则鼓励我们通过设计更好的代码结构,使 得在添加新功能或者扩展系统时,尽量减少对现有代码的修改。 以下是一个简化的日志记录器的示例,展示了在适当情况下修改代码,也不违背开闭 原则。在这个例子中,我们的应用程序支持将日志输出到控制台和文件。假设我们需 要添加一个新功能,以便在输出日志时同时添加一个时间戳。

原始代码:
interface Logger {
void log ( String message );
}
class ConsoleLogger implements Logger {
@Override
public void log ( String message ) {
System . out . println ( "Console: " + message ); }
}
class FileLogger implements Logger {
@Override
public void log ( String message ) {
System . out . println ( "File: " + message );
// 将日志写入文件的实现省略
}
}

为了添加时间戳功能,我们需要修改现有的 ConsoleLogger 和 FileLogger 类。 虽然我们需要修改代码,但由于这是对现有功能的改进,而不是添加新的功能,所以 这种修改是可以接受的,不违背开闭原则。 

修改后的代码:
interface Logger {
void log ( String message );
}
class ConsoleLogger implements Logger {
@Override
public void log ( String message ) {
String timestamp =
LocalDateTime . now (). format ( DateTimeFormatter . ISO_LOCAL_DATE_TIME );
System . out . println ( "Console [" + timestamp + "]: " + message );
}
}
class FileLogger implements Logger {
@Override
public void log ( String message ) {
String timestamp =
LocalDateTime . now (). format ( DateTimeFormatter . ISO_LOCAL_DATE_TIME );
String logMessage = "File [" + timestamp + "]: " + message ;
System . out . println ( logMessage );
// 将日志写入文件的实现省略
}
}

在这个例子中,我们只是对现有的日志记录器类进行了适当的修改,以添加时间戳功能。这种修改不会影响到其他部分的代码,因此不违背开闭原则。总之,适当的修改 代码并不一定违背开闭原则,关键在于我们如何权衡修改的影响和代码设计。 

当我们遵循开闭原则时,其目的是为了让我们的代码更容易维护、更具可复用性,同时降低了引入新缺陷的风险。但是,在某些情况下,遵循开闭原则可能会导致过度设计,增加代码的复杂性。因此,在实际开发中,我们应该根据实际需求和预期的变化来平衡遵循开闭原则的程度。写代码不是为了设计而设计,脱离需求谈设计都是耍流氓,有些场景,比如项目的使用频率不高,修改的可能性很低,或者代码本来就很简单使用了设计模式可能会增加开发难度,提升开发成本,反而得不偿失。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/18158.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows基础命令

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一.目录和文件的操作 1.cd 命令 切换到d盘 2.目录分为相对路径和绝对路径 3. dir命令 用于显示目录和文件列表 4. md 或 mkdir 创建目录 5. rd 用于删…

数据结构——AVL树

文章目录 一.AVL树的定义二.AVL树的插入三.插入后更新平衡因子四.AVL树的旋转1.左单旋2.右单旋3.先左单旋再右单旋4.先右单旋再左单旋 五.检查是否满足AVL树六.源码 一.AVL树的定义 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支…

智慧水务和物联网智能水表在农村供水工程中的应用

摘 要:随着社会的进步和各项事业的飞速发展,人民生活水平的逐步提升,国家对农村饮水安全有了更高的要求,为了进一步提升农村供水服务的质量,利用现代化、信息化科学技术提升农村供水服务质量,提高用水管理效…

基于高通QCC5171的对讲机音频数据传输系统设计

一 研发资料准备 二 设计方法 蓝牙连接与配对:使用QCC5171的蓝牙功能,实现设备之间的蓝牙连接和配对。确保设备能够相互识别并建立起稳定的蓝牙连接。 音频采集与处理:将麦克风采集到的音频数据通过QCC5171的ADC(模数转换器&…

upload-labs详解------持续更新

目录 注: 搭建: pass-01(前端绕过) pass-02(后缀绕过) pass-03(黑名单绕过) pass-04(Apache解析漏洞\.htaccess文件绕过) 注: 本项目提供的…

Vc - Qt - Qt::KeepAspectRatio及Qt.SmoothTransformation

Qt::KeepAspectRatio是一个枚举值,用于指定图像的缩放行为。设置Qt::KeepAspectRatio属性后,图像将按比例缩放以适应目标矩形,并保持其长宽比。如果目标矩形的宽高比与图像的宽高比不一致,则图像的一部分会被剪裁掉。 Qt::SmoothT…

如祺出行冲刺自动驾驶商业化,人少的地方机会多?

网约车,正在迎来让人“不明觉厉”的新一轮竞赛。 网约车监管信息交互系统的数据显示,截至今年6月30日,全国共有318家网约车平台公司取得网约车平台经营许可,环比增加5家;网约车监管信息交互系统6月份共收到订单信息7.…

记一道有趣的sql题

有一张运单表:dwd_biz_waybill_td,该表的主键是way_bill_id,并且有如下字段: way_bill_id(运单表主键),shiping_date(下单日期,时间格式为yyyy-MM-dd)&#…

爬虫原理详解及requests抓包工具用法介绍

文章目录 一、什么是爬虫?二、爬虫的分类三、网址的构成四、爬虫的基本步骤五、动态页面和静态页面六、伪装请求头七、requests库介绍1. 概念:2. 安装方式(使用镜像源):3. 基本使用:4. response对象对应的方…

使用Express部署Vue项目

使用Express部署Vue项目 目录 1. 背景 2. 配置Vue CLI 1.1 安装nodejs 1.2 创建vue-cli 1.3 创建vue项目 1.4 构建vue项目3. 配置Express 2.1 安装express 2.2 创建项目4. 使用express部署vue项目 1,背景 我们想要做一个前后端分离的课程项目,前端…

eclipse版本与jdk版本对应关系

官网:Eclipse/Installation - Eclipsepedia eclipse历史版本(2007-):Older Versions Of Eclipse - Eclipsepedia Eclipse Packaging Project (EPP) Releases | Eclipse Packages

ARM裸机-10

1、X210开发板和光盘资料 1.1、配置信息 CPU:三星S5PV210 内存:512M DDR2 SDRAM Flash:4GB iBand LCD:7寸,分辨率800x480 触摸屏:电容触摸屏 2、X210开发板硬件手册 3、X210开发板刷系统 3.1、什么是刷…

记一次centos 磁盘挂载过程

前言 最近买了云服务器磁盘,需要挂载,一下就由大猿来记录这次过程。 挂载过程 查看磁盘挂载情况 查看物理硬盘 lsblkfdisk -l标记分区 fdisk /dev/vdb格式化分区 xfs mkfs.xfs /dev/vdb mkfs.xfs -f /dev/vdbext4 mkfs.ext4 /dev/vdbxfs 和 ex…

一起学算法(顺序表篇)

概念: 1.顺序表的定义 用一段地址连续的存储单元依次存储数据的线性表被称为数据表,在Java中顺序表一般是数组或者是ArrayList实现的 先把代码放这里,接下来一一给大家进行讲解: public class SeqList {private Object[] data;…

网络基础-认识每层的设备和每层的特点用途

目录 网络层次常见设备各层介绍数据链路层网络层传输层应用层 网络层次 常见设备 各层介绍 数据链路层 有了MAC地址。数据链路层工作在局域网中的,以帧为单位进行传输和处理数据。 网络层 网络层有了IP。不同的网络通过路由器连接成为互联网 路由器的功能:   …

如何用C#实现上位机与下位机之间的Wi-Fi通信?

有IP协议支持的话用UDP报文或者TCP直接发IP地址和端口不行么?你说的WiFi难道是2.4GHz频率模块那种东东? 你既然用了wifi,那么只要上位机和下位机的对应wifi网卡都具有ip地址以及其协议支持,那么和网络编程没啥子明显区别的吧………

pycharm制作柱状图

Bar - Bar_rotate_xaxis_label 解决标签名字过长的问题 from pyecharts import options as opts from pyecharts.charts import Barc (Bar().add_xaxis(["高等数学1,2","C语言程序设计","python程序设计","大数据导论",…

【13】STM32·HAL库-正点原子SYSTEM文件夹 | SysTick工作原理、寄存器介绍 | printf函数使用、重定向

目录 1.sys文件夹介绍(掌握)2.deley文件夹介绍(掌握)2.1deley文件夹函数简介2.2SysTick工作原理2.3SysTick寄存器介绍2.4delay_init()函数(F1)2.5delay_us()函数(F1)2.6delay_ms()函…

十大排序|十大排序

稳定排序&#xff1a;冒泡排序、插入排序、归并排序、基数排序、桶排序 不稳定排序&#xff1a;选择排序、快速排序、希尔排序、堆排序 二、插入排序&#xff1a; 代码&#xff1a; #include<iostream> #include<cstdio> #include<stdlib.h> #include<ve…

NLP实战9:Transformer实战-单词预测

目录 一、定义模型 二、加载数据集 三、初始化实例 四、训练模型 五、评估模型 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] 模型结构图&#xff1a; &a…