设计模式系列-第二章(设计原则-SOLID)_设计模式第二章_素履之往、、的博客-程序员宅基地

技术标签: 设计模式  

前言:SOLID 原则并非单纯的 1 个原则,而是由 5 个设计原则组成的,它们分别是:单一职责原则、开闭原则、里式替换原则、接口隔离原则和依赖反转原则,依次对应 SOLID 中的 S、O、L、I、D 这 5 个英文字母。

一、单一职责原则(SRP)

       单一职责原则的英文是 Single Responsibility Principle,缩写为 SRP。一个类或者模块只负责完成一个职责(或者功能)。单一职责原则是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。

判断是否单一职责

  • 类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
  • 类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
  • 私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
  • 比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的 Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
  • 类中大量的方法都是集中操作类中的某几个属性,比如,在 UserInfo 例子中,如果一半的方法都是在操作 address 信息,那就可以考虑将这几个属性和对应的方法拆分出来。

二、开闭原则(OCP)    

       开闭原则的英文全称是 Open Closed Principle,简写为 OCP。详细表述一下,那就是,添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)即对扩展开放、对修改关闭。

       添加一个新功能,不可能任何模块、类、方法的代码都不“修改”,这个是做不到的。类需要创建、组装、并且做一些初始化操作,才能构建成可运行的的程序,这部分代码的修改是在所难免的。我们要做的是尽量让修改操作更集中、更少、更上层,尽量让最核心、最复杂的那部分逻辑代码满足开闭原则。回到这条原则的设计初衷:只要它没有破坏原有的代码的正常运行,没有破坏原有的单元测试,我们就可以说,这是一个合格的代码改动。

       很多设计原则、设计思想、设计模式,都是以提高代码的扩展性为最终目的的。特别是 23 种经典设计模式,大部分都是为了解决代码的扩展性问题而总结出来的,都是以开闭原则为指导原则的。最常用来提高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(比如,装饰、策略、模板、职责链、状态)。

       基于一定的粒度(例如模块,类,属性等),扩展是平行地增加,修改是变更更细粒度的子集。扩展和修改和具体的粒度有关。不同的粒度下,扩展和修改定义不同。我个人以为,扩展的结果是引入了更多的平行结构(例如相似的派生类handler),以及支持这些平行结构的代码(利用多态,在关键的地方使用接口)。这些引入会让代码结构变的扁平一些,但是也更晦涩一些。修改,往往会增加代码的深度(这里指更低粒度的复杂度),例如,文中log例子,修改后,check函数有五个参数,内部的if else逻辑更多。但是,如果从参数以及if作用域的角度,这也可算作扩展。所以,扩展还是修改更本质的区别在于修改发生的粒度和层次。通常偏好修改发生在更高的层次上,这要求我们能够用接口和组合把系统合理的切分,做到高内聚和低耦合。高内聚可以让修改发生在更高层次上,替换掉整个低层次实现细节。低耦合,可以让模块之间的调用最小化,可以让高层次的修改最小化。支持高层次的平行结构不是免费的,除非有明确的收益(例如文中隔离Kafka实现细节的例子),不然还是让重构等待到需要的那一刻,预测未来的大部分平行结构其实不会被真正用到。

三、里式替换原则(LSP)

        里式替换原则的英文翻译是:Liskov Substitution Principle,缩写为 LSP。

        里式替换原则是用来指导,继承关系中子类该如何设计的一个原则。理解里式替换原则,最核心的就是理解“design by contract,按照协议来设计”这几个字。父类定义了函数的“约定”(或者叫协议),那子类可以改变函数的内部实现逻辑,但不能改变函数原有的“约定”。这里的约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。理解这个原则,我们还要弄明白里式替换原则跟多态的区别。虽然从定义描述和代码实现上来看,多态和里式替换有点类似,但它们关注的角度是不一样的。多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。而里式替换是一种设计原则,用来指导继承关系中子类该如何设计,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑及不破坏原有程序的正确性。

四、接口隔离原则(ISP)

        接口隔离原则的英文翻译是“ Interface Segregation Principle”,缩写为 ISP。直译成中文的话就是:客户端不应该被强迫依赖它不需要的接口。其中的“客户端”,可以理解为接口的调用者或者使用者。

        如果把“接口”理解为一组接口集合,可以是某个微服务的接口,也可以是某个类库的接口等。如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口。如果把“接口”理解为单个 API 接口或函数,部分调用者只需要函数中的部分功能,那我们就需要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数。

接口隔离原则与单一职责原则的区别

       单一职责原则针对的是模块、类、接口的设计。接口隔离原则相对于单一职责原则,一方面更侧重于接口的设计,另一方面它的思考角度也是不同的。接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。

五、依赖反转原则(DIP)

       依赖反转原则。依赖反转原则的英文翻译是 Dependency Inversion Principle,缩写为 DIP。依赖反转原则也叫作依赖倒置原则。这条原则跟控制反转有点类似,主要用来指导框架层面的设计。高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。

举例就是tomcat和Web 应用程序代码。Tomcat 和应用程序代码之间并没有直接的依赖关系,两者都依赖同一个“抽象”,也就是 Servlet 规范。Servlet 规范不依赖具体的 Tomcat 容器和应用程序的实现细节,而 Tomcat 容器和应用程序依赖 Servlet 规范。

控制反转(IOC)

控制反转的英文翻译是 Inversion Of Control,缩写为 IOC。

控制反转是一个比较笼统的设计思想,并不是一种具体的实现方法,一般用来指导框架层面的设计。这里所说的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。
控制反转是一种编程思想,把控制权交给第三方。依赖注入是实现控制反转最典型的方法。

依赖注入(DI)

依赖注入是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/king13127/article/details/115326631

智能推荐

导入Microsoft.Office.Interop.Word.dll后,运行报错-程序员宅基地

调试程序,报错如下:错误 1 无法从程序集“g:\aabbcc\bin\Debug\Microsoft.Office.Interop.Word.dll”中嵌入互操作类型,因为该程序集缺少“ImportedFromTypeLibAttribute”特性或“PrimaryInteropAssemblyAttribute”特性 。解决方案:右键该dll文件: 【属性】: 嵌入互操作类型 True...

负载均衡——LVS-DR模式+keepalived部署实战(原理分析及详细实操)_lvsdr keepalived https-程序员宅基地

文章目录前言一:LVS-DR工作原理1.1:DR模式概述1.2:如何分析LVS-DR数据包流向?1.3:LVS-DR中存在的ARP问题3.4:解决上述两个ARP问题的方法二:LVS-DR部署2.1:实验环境2.2:实验拓扑图2.3:实验目的2.4:实验过程2.4.1:调度器配置2.4.2:配置两台节点IP地址绑定VIP2.4.2:配置两台节点IP地址绑定VIP2.4.3:节点服务器配置抑制ARP响应2.4.4:两台节点服务器配置测试网址2.5:实验验证三:keepalived实现原理3.1:keepaliv_lvsdr keepalived https

php监测mysql数据库更新_php监测数据是否成功插入到Mysql数据库的方法_蓝狍的博客-程序员宅基地

在PHP中该怎么用代码判断数据是否成功插入到Mysql数据库?这篇文章中给出了两种解决的方法,大家可以根据需要选择相对应的方法,有需要的朋友们下面来跟着小编一起学习学习吧。前言本文主要介绍的是php代码中监测数据是否成功插入到Mysql数据库,可以使用这两种方式。下面话不多说,来看看详细的解决方法。解决方法第一种就是通过mysql_query()函数来判断:if(mysql_query('inse..._"php $db->insert(\". 检测是否写入数据库"

webpack打包js文件的问题_webpack打包的js不执行_秋刀prince的博客-程序员宅基地

前言这里记录下webpack打包js文件的配置和遇到的问题。因环境版本等不同,解决办法未必都是通用的,仅作为大家参考之用;我的环境配置macOS M1 系统webpack版本:(全局版本)webpack 5.49.0,webpack-cli 4.7.2node版本:v14.17.4开发工具:webstorm学习自建目录结构dist目录下存放的是我们webpack打包后的最终js输出文件node_modules 是我们安装项目本地的webpack后,目录自动生成的,全局和本地的webpa_webpack打包的js不执行

2014年-12月 CCF-5 货物调度-程序员宅基地

题意:某公司要处理一个周期性的物流问题。  有n个城市,第i个城市在每周的第j(1≤j≤7) 天会生产aij吨某种货物,同时需要消耗bij吨该种货物。已知每周的产量等于消耗量(即aij之和等于bij之和)。  城市之间有m条道路,第k条道路连接了城市sk和tk。一条道路上运输1吨货物有一个固定的成本ck。道路都可以双向使用。每天运输的货物量没有限制。城市之间的距离并不远,货物可以从任意一个城市

prometheus使用mysqld-exporter监控mysql_k8s prometheus定义mysql监控项-程序员宅基地

一、安装1、被监控mysql机器安装mysqld-exportertar zxf mysqld_exporter-0.12.1.linux-amd64.tar.gz -C /data/2、添加配置文件cd /data/mysqld_exporter-0.12.1.linux-amd64vim .my.cnf[client]user=rootpassword=1234563、启动mysqld-exporternohup ./mysqld_exporter --config.my-cn_k8s prometheus定义mysql监控项

随便推点

spring framework-IOC_shuxiaohua的博客-程序员宅基地

概述备注:图片引用至http://www.51gjie.com/javaweb/981.html。spring framework架构的图片很多,上图比较贴合我个人通过阅读源码认识到的各个模块的关系。通过架构图实际上看不出spring framework里面的细节,引入该结构图主要是让读者对spring framework的模块划分有一个清晰的认识。spring framework的核心就是Bean容器,其他所有功能都是基于Bean容器的扩展点进行构建的。通过慢慢的接触spring源码,你会发现,刚

在线民宿满意度测评项目[开源]-程序员宅基地

向AI转型的程序员都关注了这个号????????????机器学习AI算法工程 公众号:datayx重庆市在线民宿满意度测评项目,负责数据采集、主题抽取、情感分析等任务。开发的目的是克服...

一键关闭危险端口脚本-- windows脚本_关闭高危端口脚本-程序员宅基地

开放端口方法:https://www.cnblogs.com/zhurong/p/9398602.html以下是windows脚本,需保存为 .bat双击即可@echo offcolor 1ftitle 关闭常见的危险端口echo 正在开启Windows防火墙echo.netsh advfirewall set currentprofile state on > nul..._关闭高危端口脚本

工具类PageData的使用_new pagedata-程序员宅基地

为了方便项目开发我们往往就是会使用到一下帮助类来简化我们项目开发中的繁琐工作下面介绍一种我在一些项目使用到的一个工具类PageData的使用,可以满足项目中大部分数据传递存储使用作用详细直接上代码import java.util.Collection;import java.util.HashMap;import java.util.Iterator;import java.util...._new pagedata

Multipath多路径冗余全解_no_path_retry-程序员宅基地

一、什么是multipath普通的电脑主机都是一个硬盘挂接到一个总线上,这里是一对一的关系。而到了有光纤组成的SAN环境,由于主机和存储通过了光纤交换机连接,这样的话,就构成了多对多的关系。也就是说,主机到存储可以有多条路径可以选择。主机到存储之间的IO由多条路径可以选择。既然,每个主机到所对应的存储可以经过几条不同的路径,如果是同时使用的话,I/O流量如何分配?其中一条路径坏掉了,如何处_no_path_retry

在java中下列说法不正确的是,【单选题】关于java中的转义字符,下列说法不正确的是 A. 是制表位 B. 可以实现换行 C. "可以在页面中显示双引号 D. \可以在页面实现注释...-程序员宅基地

实际流体总流的能量方程是工程流体力学中最常用的基本方程之一,其应用条件是()。A.流动是恒定雷诺数Re反映了()的对比关系。A.黏滞力与重力B.重力与惯性力C.惯性力与黏滞力D.黏滞力与动水2017初二暑假作业答案,2017八年级地理暑假作业答案作业八管道中紊流运动,过水断面流速分布符合()。A.均匀分布B.直线变化规律C.抛物线规律D.对数曲线规2017初二暑假作业答案,2017八年级地理暑假作...