设计模式中的黄金原则:引领你的代码风格,提升可维护性与扩展性

news2024/9/23 9:24:13

中国的先贤说过: 有道无术,术可求.有术无道,止于术. 术指的是技能、技术或方法,而道指的是原则、道德、智慧和理念。

西方古代的哲人也说过同样的话: 智慧之路从感性开始,却终极于理性.为什么要说设计原则呢, 因为设计模式通常需要遵循一些设计原则,在设计原则的基础之上衍生出了各种各样的设计模式。设计原则是设计要求,设计模式是设计方案,使用设计模式的代码则是具体的实现。

d3de79f38a67a59b18e524bad01d3bf2.jpeg

设计模式中主要有六大设计原则,简称为SOLID ,是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的),六大设计原则分别如下:

1、单一职责原则(Single Responsibitity Principle)

2、开放封闭原则(Open Close Principle)

3、里氏替换原则(Liskov Substitution Principle)

4、接口分离原则(Interface Segregation Principle)

5、依赖倒置原则(Dependence Inversion Principle)

6、迪米特法则(Law Of Demter)

1) 单一职责原则

一个类应该只有一个引起它变化的原因。换言之,一个类只负责一项职责。这样可以使得类更加可维护、可扩展、可重用。

在类的设计中 我们不要设计大而全的类,而是要设计粒度小、功能单一的类

生活中的例子

首先是单一职责原则: 一个类只负责一项职责, 比如说一辆汽车的刹车踏板,它的作用就是让行进间的汽车停止的, 如果现在这个刹车踏板的功能改成了,踩一半是油门, 踩到底是刹车,大家想一下 这会出现一种什么情况呢 ? 那现在世界上会开车的人 可能就非常少了, 估计女司机应该一个都没有. 这就是单一职责原则.

2d3b1b5bf8c6a42e275997ccb7fc07ad.jpeg

代码举例: 产品提出需, 要我们设计一个计算图形面积的类,如下:

f7696279b27bdf329082ec566014942f.jpeg

接着又提出,要能够将计算结果以JSON格式打印出来,然后我们就添加了这样一个打印JSON格式的方法

4ce4729ee0e28814b15cc7f7a9aa06ce.jpeg

请问在该类中添加打印方法是否合理 ?

答: 显然是不合理. 如果后面产品有提出了 打印XML格式...导出到Excel... 那

这个类就会变得十分臃肿,增加了很多本不属于他的责任

那该如何设计? 答: 将打印功能单独设计一个类出来

93a03d835fd357f86bbb60b29c635933.jpeg

注意: 面试官问的是一个思路,看你是不是有这种设计思维 不会关心具体代码

单一职责原则的优点包括:

1. 提高代码的可读性和可维护性:一个类只负责一个职责,代码更加清晰,更容易理解和维护。

2. 降低类的复杂度:一个类只需要负责一个职责,类的复杂度更低,更容易进行测试和调试。

3. 降低代码的耦合度:当一个类只负责一个职责时,不同的职责可以分配到不同的类中,不同的类之间相互独立,从而降低了代码的耦合度。

4. 提高代码的可复用性:一个类只负责一个职责,可以更容易地被其他模块复用。

5. 便于扩展和维护:当需求变化时,如果每个类只负责一个职责,我们只需要修改相关的类即可,而不需要修改其他的类,从而更容易进行扩展和维护。

延伸面试题: 如何判断一个类的职责是否单一?

类中的代码行数、函数、或者属性过多;

函数或者属性过多,说明该类设计的不够单一,很可能包含其他职责内容

类依赖的其他类过多

说明该类有一些本该属于自己的功能,被抽取到其他类中了

私有方法过多

过多的私有方法,说明该类很可能包含其他类的职责内容

类中大量的方法都是集中操作类中的几个属性

说明属性设计不合理,本应该设计在其他类的属性

总结: 不同的应用场景、不同阶段的需求背景下,对同一个类的职责是否单一的判定,可能都是不一样的,最好的方式就是:

我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构

2) 开放封闭原则

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。也就是说,我们应该通过添加新的代码来扩展软件的功能,而不是修改已有的代码。

生活中的例子

开放封闭原则: 对扩展开发,对修改关闭. 我还是用汽车举例, 我是一个广东人, 今年冬天我要开车去东北 去东北吃雪, 因为广东人好吃嘛,没吃过雪 想去长白山吃点新鲜的雪. 东北雪很大 就需要给这个车的轮胎做防滑. 其实很简单只要给轮胎装上防滑链就可以了,这就属于是对轮胎的开放扩充. 在轮胎上面新增了防滑链,没有改变轮胎的原有功能. 我们不能因为为了给轮胎做防滑,而把汽车引擎换掉了.

944a7a68414b4dc9ede3671b17da4f96.jpeg

代码举例: 还是计算面积的功能,现在我们又有一个计算三角形面积的需求,应该怎么做呢?

方式1: 直接修改 AreaCalculator

630776a8ca0109f4ca212c1710db27a6.jpeg

上面的做法就违反了开闭原则,因为假设后面又增加了 计算正方形、圆形等等的需求的时候,就需要不断的修改该类的代码,增加新的函数.

想要满足开闭原则,就必须要使用顶层设计思维,来解决问题

顶层设计思维

抽象意识

封装意识

扩展意识

比如在这里我们先利用抽象思维,将各类图形进行抽取,设计一个接口来表示图形(抽象),然后利用扩展思维,在接口中有一个抽象的方法,该方法的功能是获取面积.

每增加一种图形,就去实现该接口,然后重写计算面积方法即可.

e57d11a346052d3580ada2cc828950b2.jpeg

有了抽象之后,就可以利用抽象,修改计算面积的方法,将参数改为接口类型,该程序的扩展性 就提升了,再有新的图形添加,也不需要修改计算程序的代码,只需添加新的类即可,从而实现了开闭原则.

f291c93e3851f273bb2fb4e8567407d2.jpeg

在写代码的时候后,我们要多花点时间往前多思考一下,这段代码未来可能有哪些需求变更、如何设计代码结构,事先留好扩展点,以便在未来需求变更的时候,不需要改动代码整 体结构、做到最小代码改动的情况下,新的代码能够很灵活地插入到扩展点上,做到“对扩 展开放、对修改关闭”。

开放封闭原则的优点包括:

1. 提高代码的可维护性和可复用性:开放封闭原则要求我们使用扩展来增加功能,而不是直接修改原有代码,这样可以避免对原有代码的破坏,从而提高代码的可维护性和可复用性。

2. 降低代码的耦合度:当需要增加新的功能时,我们可以通过扩展来实现,而不是修改原有代码,这样可以避免不必要的代码耦合,从而提高代码的灵活性和可扩展性。

3. 提高代码的稳定性:当需要增加新的功能时,我们只需要扩展已有的代码,而不需要修改原有的代码,这样可以避免引入新的错误,从而提高代码的稳定性。

4. 提高代码的可测试性:使用开放封闭原则可以避免对原有代码的破坏,从而更容易进行测试和调试。

5. 提高代码的可维护性:使用开放封闭原则可以使得代码更加模块化,更容易进行维护和修改。

3) 接口隔离原则

客户端不应该依赖它不需要的接口。也就是说,我们应该将不同的接口拆分成更小的、更具体的接口,从而避免客户端依赖于它们不需要的方法。这样可以降低接口的复杂性,提高系统的可维护性和可扩展性。

生活中的例子

接口隔离原则: 客户端不应该依赖它不需要的接口。 我们拿汽车的方向盘举例, 我们通过转动方向盘,可以控制车辆的转向,对于我们开车的人来说只需要知道如何转动方向盘就可以了 , 例如 倒车入库: 1、左后视镜下沿与停止线重合,向右打死方向盘;2、看右后视镜,车身库角30cm到了,向左回一圈方向盘;3、当车门把手与库角重合,方向盘右打死;4、看左侧后视镜,看到后边库角露出10cm,方向盘回正。

用户操作方向盘并不需要知道车辆的引擎、刹车、变速器等部件的具体实现细节,它只需要提供一个简单的接口 就是方向盘,即让驾驶员可以方便地控制车辆的方向。

d59ed020ab8d9ace7ec12801ba7e1229.jpeg

下面的代码,就是没有遵守接口隔离原则,其中Rectangle类 实现了不应该它实现的方法.

848e0989fd8e976a859469d440e839c2.jpeg

接口隔离原则的优点包括:

提高代码的灵活性和可维护性:接口隔离原则要求我们定义精简的接口,这样可以使得代码更加灵活和可维护,因为我们只需要实现我们真正需要的接口即可。

2. 提高代码的可测试性:接口隔离原则可以使得代码更加容易进行测试和调试,因为我们可以只测试我们真正需要的接口。

降低模块之间的耦合度:当模块之间只依赖于真正需要的接口时,它们之间的耦合度更低,更容易进行组合和修改。

4. 提高代码的可复用性:当接口精简清晰时,代码的可复用性也会提高,因为我们可以更容易地将代码组合到不同的应用场景中。

5. 提高代码的安全性:接口隔离原则可以避免一些意外的依赖关系,从而提高代码的安全性。

延伸面试题: 接口隔离原则与单一职责原则的区别

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。

单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

4) 依赖倒置原则

高层模块不应该依赖于底层模块,而是应该依赖于抽象。也就是说,我们应该面向接口编程,而不是面向实现编程。

我们一起看一下下面代码有没有问题 ?

fa94cd0f09d760967301dcd31c74b1d9.jpeg

很显然上面的代码是有问题的, 我们应该遵守依赖倒置原则,高层模块不应该依赖于底层模块,而是应该依赖于抽象 .Car类是相对高层的,而QYEngine是底层的是具体实现.

e46feb39c1fde0c7527be85c4e8b073c.jpeg

依赖倒置原则的核心思想是:针对抽象编程,而不是针对具体实现编程。这样可以降低模块之间的耦合度,使系统更加灵活、可扩展和易于维护。同时,依赖倒置原则也可以促进面向对象设计的另一个原则——开闭原则的实现,即可以在不修改已有代码的情况下,通过添加新的实现来扩展系统的功能。

依赖倒置原则的优点包括:

1. 提高代码的灵活性和可维护性:依赖倒置原则要求我们依赖于抽象而不是具体实现,这样可以使得代码更加灵活、可扩展和可维护。

2. 降低模块之间的耦合度:当模块之间依赖于抽象而不是具体实现时,它们之间的耦合度更低,更容易进行组合和修改。

3. 提高代码的可测试性:依赖倒置原则可以使得代码更加容易进行测试和调试,因为我们可以使用抽象来代替具体实现,从而更容易进行模拟和测试。

4. 提高代码的可复用性:依赖倒置原则可以使得代码更加容易被复用,因为我们可以使用抽象来代替具体实现,从而更容易将代码组合到不同的应用场景中。

5. 提高代码的可扩展性:依赖倒置原则可以使得代码更加容易进行扩展,因为我们可以通过添加新的实现类来扩展代码的功能,而不需要修改已有的代码。

延伸面试题: 关于依赖倒置原则、依赖注入、控制反转这三者之间的区别与联系

1 ) 依赖倒置原则

依赖倒置是一种通用的软件设计原则, 主要用来指导框架层面的设计。

高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。

2 ) 控制反转

控制反转与依赖倒置有一些相似, 它也是一种框架设计常用的模式,但并不是具体的方法。

“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。

Spring框架,核心模块IoC容器,就是通过控制反转这一种思想进行设计的

3 ) 依赖注入

依赖注入是实现控制反转的一个手段,它是一种具体的编码技巧。

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

依赖注入真正实现了面向接口编程的愿景,可以很方便地替换同一接口的不同实现,而不会影响到依赖这个接口的客户端。

5) 里氏替换原则

子类必须能够替换掉它们的父类。也就是说,在任何使用父类的地方,都应该能够使用子类来替代,而且程序不应该出现任何错误或异常

下面是一个 Rectangle 类,它是用表示矩形的,它有一个获取面积的方法

e501317ad91b4161de197396bae98d26.jpeg

接下来再设计一个 Square 类继承自 Rectangle 类表示正方形,该类的构造方法中只接收一个边长即可,但是要将继承子父类的长和宽属性 都进行填充.

并且 在 Square 类中,我们重写了父类 Rectangle 中的 setWidth 和setHeight 方法,这样可以确保 Square 的长和宽始终相等

ba834d48ff84665980e8eb3e0c2ed04e.jpeg

在测试方法中,我们创建了一个 Square 对象并将其赋值给一个 Rectangle 类型的变量 squareAsRectangle 。最后,我们调用了 printArea 方法来打印矩形和正方形的面积,可以看到程序输出正确的结果。

72b3d4e1d7bebdbdbc88278f00146770.jpeg

这个例子展示了里式替换原则的一个基本思想:子类应该能够替换掉父类并且不会影响程序的正确性。在这个例子中,我们可以将 Square 对象视为Rectangle 对象来使用,因为它们都有相同的方法和属性。这就是里式替换原则的应用。

a151abf868aec3bf5d5a692b4127ec1a.jpeg

里氏替换原则的优点包括:

1. 提高代码的灵活性:通过遵循里氏替换原则,我们可以在不影响程序正确性的情况下,更加灵活地使用不同的子类对象来实现不同的功能。

2. 提高代码的可扩展性:通过遵循里氏替换原则,我们可以更容易地向系统中添加新的子类,从而实现代码的扩展。

3. 提高代码的可维护性:通过遵循里氏替换原则,我们可以更容易地修改子类的实现,从而提高代码的可维护性。

4. 提高代码的可读性:通过遵循里氏替换原则,我们可以使得代码更加符合人类的思维方式,从而提高代码的可读性。

5. 降低代码的耦合度:通过遵循里氏替换原则,我们可以降低代码的耦合度,使得各个模块之间的关系更加清晰,更容易进行组合和修改。

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

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

相关文章

Docker GitLab-Runner安装

Docker GitLab-Runner安装 GitLab-Runner安装 问题合集GitLab 域名的配置修改Runner容器内注册失败,提示 dial tcp: lookup home.zsl0.com on 192.168.254.2:53: no such host GitLab-Runner 安装 拉去gitlab/gitlab-runner镜像 docker pull gitlab/gitlab-runne…

【STL】:vector用法详解

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关vector的基础用法,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数…

什么是jquery

jquery是一个javascript库&#xff1b;用来简化javascript编程&#xff1b;基本是前端必备&#xff1b; 看一下示例&#xff1b; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <script src"https://cdn.staticfile.org/j…

智慧燃气:智慧燃气推进设备全面感知及协同运营

关键词&#xff1a;智慧燃气、燃气数字化、数字燃气、智能燃气、智慧燃气建设 智慧燃气建设应用技术迭代与试点推进方面的问题。随着大数据、人工智能、物联网等新兴前沿技术不断推进和产业化&#xff0c;以及国家工业和信息化部、住房和城乡建设部、国务院国资委等部委大力推…

WMS仓储管理系统在电商领域的应用与影响

随着电商行业的快速发展&#xff0c;消费者对购物体验的要求也越来越高。而在电商运营中&#xff0c;仓储管理系统的应用对于提升购物体验和销售业绩有着重要的作用。本文将探讨WMS仓储管理系统在电商行业的应用与影响。 WMS&#xff08;Warehouse Management System&#xff0…

实战经验分享:打造千万级直播项目,如何选择适合的长连接技术,告别CRUD开发

前言 其实不管大厂、小厂&#xff0c;做业务开发的同学都知道&#xff0c;写一个功能&#xff0c;有中台&#xff0c;有架构&#xff0c;有API&#xff0c;有SDK&#xff0c;很多可复用的代码直接调一下RPC接口或者一个注解就搞定了复杂的操作&#xff0c;所以很多螺丝钉们都没…

对python中切片详解

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 Python中什么可以切片 Python中符合序列的有序序列都支持切片(slice) 如:列表,字符,元祖 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视…

YOLO目标检测——人体行为数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;在人流量统计、行人闯入/越界检测、人群密集/拥堵检测预警等场景中&#xff0c;对监控场景中是否存在人体进行检测&#xff0c;并精确定位、追踪。数据集说明&#xff1a;&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;含…

day49--动态规划8

139.单词拆分 关于多重背包&#xff0c;你该了解这些&#xff01; 背包问题总结篇&#xff01; 第一题&#xff1a;单词拆分 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict&#xff0c;判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 说明&#xff1…

[Database] MySQL 8.x Window / Partition Function (窗口/分区函数)

&#x1f9f2;相关文章 [1] MySQL 系统表解析以及各项指标查询 [2] MySQL 5.7 JSON 字段的使用的处理 [3] MySQL经典练习50题 简介 MySQL 8.0版本开始支持窗口函数 官方文档 在之前的版本中已存在的大部分聚合函数&#xff0c;在MySQL 8 中也可以作为窗口函数来使用 方法 / …

地址空间收尾-进程控制

子进程为什么能继承父进程的环境变量呢&#xff1f;并且具有全局属性&#xff1f; 通过打印地址实验可以发现&#xff0c;进程的环境变量在栈区之上 父进程的环境变量也是由bash继承下来的&#xff0c;必定有页表帮助我们从虚拟到物理的映射&#xff0c;创建子进程的页表时也会…

PowerCLI 实现email发送消息

前提条件 开启wmi,配置网卡,参考 创建发送获取信息到邮件 最终实现获取esxi 主机版本信息, esxi 主机IP,esxi开机开启状态,虚拟机名称,虚拟机IP,虚拟机开机状态,虚拟机归属哪一个物理机,发送信息到邮箱。脚本解释以下全部注释解释,请你在#部分获取。powercli-send-e…

HTML页面获取URL传递的参数值

如&#xff1a; // 查询url上链接的参数与参数值 function getQueryString(name) {var url window.location.search; // 获取URLvar pattern new RegExp("[\?\&]" name "([^\&])", "i"); // 正则匹配URLvar matcher pattern.exec(…

村田:什么是UWB无线通信?使用UWB的定位方法有哪些应用 ​

UWB是Ultra-Wide Band的缩写&#xff0c;意思是超宽带。UWB无线通信是使用超宽带的频率带宽的无线通信&#xff0c;其主要特征是能够实现高精度定位。近年来&#xff0c;已普及至智能手机防丢失、高阶汽车智能钥匙等民用设备。预计未来将在多个领域普及。 UWB无线通信技术有哪些…

浙江环保用电计量adw300-hj治污产污生产设备监测

浙江环保用电计量表&#xff0c;浙江环保用电能表&#xff0c;浙江环保督查计量电表&#xff0c;环保设备能耗采集表 企业基本信息 企业名称&#xff1a;XXXXXXXXXXX 企业地址&#xff1a;XX省XX市 工 程 量&#xff1a;X台监测仪表 预计工期&#xff1a;X天 监测点位信息…

Git撤销已经push到远程分支的commit

有时想要撤销已经push到远程仓库的commit&#xff0c;将代码还原为commit之前的样子&#xff0c;应该如何做呢&#xff1f; 如果只有自己使用的分支&#xff1a;可以使用git reset命令 git log 查看需要还原的commitId&#xff0c;如下截图&#xff0c; 我们需要撤销ffe4a的…

【API篇】十、生成Flink水位线

文章目录 1、水位线的生成原则2、有序流内置水位线3、乱序流内置水位线4、自定义周期性水位线生成器5、自定义断点式水位线生成器6、从数据源中发送水位线 1、水位线的生成原则 水位线出现&#xff0c;即代表这个时间之前的数据已经全部到齐&#xff0c;之后不会再出现之前的数…

具有独特底部轮廓的剥离光刻胶的开发

引言 金属图案的剥离方法已广泛应用于各种电子器件的制造过程中&#xff0c;如半导体封装、MEMS和LED的制造。与传统的金属刻蚀方法不同的是&#xff0c;采用剥离法的优点是节省成本和工艺简化。在剥离过程中&#xff0c;经过涂层、曝光和开发过程后&#xff0c;光刻胶会在晶片…

最简单的Ubuntu更新Nvidia驱动的方法 解决nvidia-smi 不是内部或外部命令

在装有Ubuntu/Linux的服务器上遇到nvidia-smi不是内部或外部命令的问题&#xff0c;解决方法&#xff1a;更新英伟达驱动。 Ubuntu自带的“软件和更新”里可以方便地更新驱动&#xff0c;不需要从英伟达官网下载驱动。 我把它改成了第一项&#xff08;原来是一个名为nouveau的…