工作那么久,才知道的 SOLID 设计原则
认识 SOLID 原则
无论是软件系统设计,还是代码实现,遵循有效和明确的设计原则,都利于系统软件灵活可靠,安全快速的落地,更重要的是能灵活地应对需求,简化系统扩展和维护,避免无效的加班。本文主要讨论面向对象软件开发中最流行的设计原则- SOLID,它是五个设计原则为了方便记忆而组成的首字母缩写:
- 单一职责原则
- 开放/封闭原则
- 里式替换原则
- 接口隔离原则
- 依赖倒置原则
S:单一职责原则 (SRP)
基本概念
单一职责原则 (SRP) 英文全称为 Single Responsibility Principle,是最简单,但也是最难用好的原则之一。它的定义也很简单:对于一个类而言,应该仅有一个引起它变化的原因。其中变化的原因就表示了这个类的职责,它可能是某个特定领域的功能,可能是某个需求的解决方案。
这个原则表达的是不要让一个类承担过多的责任,一旦有了多个职责,那么它就越容易因为某个职责而被更改,这样的状态是不稳定的,不经意的修改很有可能影响到这个类的其他功能。因此,我们需要将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,不同类之间的变化互不影响。
实例说明
举一个具体的例子,有一个用于实现编辑和打印报表的类,这样的类存在两个变化的原因:第一,报表的内容可以改变(编辑)。第二,报表的格式可以改变(打印)。如果有一个对于报表编辑流程的修改,而报表的编辑流程会导致公共状态或者依赖关系的改变,使得打印功能的代码无法工作。所以单一职责原则认为这两个变化的原因事实上是两个分离的功能,它们应该分离在不同的类中。
相关设计模式
面对违背单一职责原则的程序代码,我们可以利用外观模式,代理模式,桥接模式,适配器模式,命令模式对已有设计进行重构,实现多职责的分离。
小结
单一职责原则用于控制类的粒度大小,减少类中不相关功能的代码耦合,使得类更加的健壮;另外,单一职责原则也适用于模块之间解耦,对于模块的功能划分有很大的指导意义。
O:开闭原则 (OCP)
基本概念
开闭原则 (OCP) 英文全称为 Open-Closed Principle,基本定义是软件中的对象(类,模块,函数等)应该对于扩展是开放的,但是对于修改是封闭的。这里的对扩展开放表示这添加新的代码,就可以让程序行为扩展来满足需求的变化;对修改封闭表示在扩展程序行为时不要修改已有的代码,进而避免影响原有的功能。
要实现不改代码的情况下,仍要去改变系统行为的关键就是抽象和多态,通过接口或者抽象类定义系统的抽象层,再通过具体类来进行扩展。这样一来,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,达到开闭原则的要求。
实例说明
同样,举个例子来更深刻地理解开闭原则:有一个用于图表显示的 Display 类,它能绘制各种类型的图表,比如饼状图,柱状图等;而需要绘制特定图表时,都强依赖了对应类型的图表,Display 类的内部实现如下:
public void display(String type) {
if (type.equals("pie")) {
PieChart chart = new PieChart();
chart.display();
} else if (type.equals("bar")) {
BarChart chart = new BarChart();
chart.display();
}
}
基于上述的代码,如果需要新增一个图表,比如折线图 LineChart ,就要修改 Display 类的 display() 方法,增加新增的判断逻辑,很显然这样的做法违反开闭原则。而让类的实现符合开闭原则的方式就是引入抽象图表类 AbstractChart,作为其他图表的基类,让 Display 依赖这个抽象图表类 AbstractChart,然后通过 Display 决定使用哪种具体的图表类,实现代码变成了这样:
private Abstractchart chart;
public void display() {
chart.display();
}
现在我们需要新增折线图显示,在客户端向 Display 中注入一个 LineChart 对象即可,无须修改现有类库的源代码。
相关设计模式
面对违背开闭原则的程序代码,可以用到的设计模式有很多,比如工厂模式,观察者模式,模板方法模式,策略模式,组合模式,使用相关设计模式的关键点就是识别出最有可能变化和扩展的部分,然后构造抽象来隔离这些变化。
小结
有了开闭原则,面向需求的变化就能进行快速的调整实现功能,这大大提高系统的灵活性,可重用性和可维护性,但会增加一定的复杂性。
L: 里式替换原则 (LSP)
基本概念
里式替换原则 (LSP) 英文全称为 Liskov Substitution Principle,基本定义为:在不影响程序正确性的基础上,所有使用基类的地方都能使用其子类的对象来替换。这里提到的基类和子类说的就是具有继承关系的两类对象,当我们传递一个子类型对象时,需要保证程序不会改变任何原基类的行为和状态,程序能正常运作。
实例说明
为了能理解里式替换原则,这里举一个经典的违反里式替换原则的例子:正方形/长方形问题。
上图为正方形/长方形问题的类层次结构,Square 类继承了 Rectangle 类,但是 Rectangle 类的宽高可以分别修改,但是 Suqare 类的宽高则必须一同修改。如果 User 类操作 Rectangle 类时,但实际对象是 Suqare 类型时,就会造成程序的出错,如下方代码:
Rectangle r = ...; // 返回具体类型对象
r.setWidth(5);
r.setHeight(2);
assert(r.area() == 10);
当返回具体类型对象为 Suqare 类型,面积为 10 的断言就是失败,这样明显是不符合里式替换原则的。
小结
要让程序代码符合里式替换原则,需要保证子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法,换句话就是子类可以扩展父类的功能,但不能改变父类原有的功能。
另一方面,里式替换原则也是对开闭原则的补充,不仅适用于继承关系,还适用于实现关系的设计,常提到的 IS-A 关系是针对行为方式来说的,如果两个类的行为方式是不相容,那么就不应该使用继承,更好的方式是提取公共部分的方法来代替继承。
I:接口隔离原则 (ISP)
基本概念
接口隔离原则 (ISP) 英文全称为 Interface Segregation Principle,基本定义:客户端不应该依赖那些它不需要的接口。客户端应该只依赖它实际使用的方法,因为如果一个接口具备了若干个方法,那就意味着它的实现类都要实现所有接口方法,从代码结构上就十分臃肿。
实例说明
现在我们看下一个违反接口隔离原则的例子,从上面类结构图中,有多个用户需要操作 Operation 类。如果 User1 只需要使用 operation1 方法,User2 只需要使用 operation2 方法,User3 只需要使用 operation3 方法,那么很明显对于 User1 来说,不应该看到 operation2 和 operation3 这两个方法,要减少对自己不关心的方法的依赖,防止 Operation 类中 operation2 和 operation3 方法的修改,影响到 User1 的功能。这个问题可以通过将不同的操作隔离成独立的接口来解决,具体如下图所示。
基于接口隔离原则,我们需要做的就是减少定义大而全的接口,类所要实现的接口应该分解成多个接口,然后根据所需要的功能去实现,并且在使用到接口方法的地方,用对应的接口类型去声明,这样可以解除调用方与对象非相关方法的依赖关系。总结一下,接口隔离原则主要功能就是控制接口的粒度大小,防止暴露给客户端无相关的代码和方法,保证了接口的高内聚,降低与客户端的耦合。
D:依赖倒置原则 (DIP)
基本概念
依赖倒置原则 (DIP) 英文全称 Dependency Inversion Principle, DIP),基本定义是:
- 高层模块不应该依赖低层模块,应该共同依赖抽象;
- 抽象不应该依赖细节,细节应该依赖抽象。
这里的抽象就是接口和抽象类,而细节就是实现接口或继承抽象类而产生的类。
实例说明
如何理解“高层模块不应该依赖低层模块,应该共同依赖抽象”呢?如果高层模块依赖于低层模块,那么低层模块的改动很有可能影响到高层模块,从而导致高层模块被迫改动,这样一来让高层模块的重用变得非常困难。
而最佳的做法就如上图一样,在高层模块构建一个稳定的抽象层,并且只依赖这个抽象层;而由底层模块完成抽象层的实现细节。这样一来,高层类都通过该抽象接口使用下一层,移除了高层对底层实现细节的依赖。
相关设计模式
关于依赖倒置原则,可以用到的设计模式有工厂模式,模板方法模式,策略模式。
小结
依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。同时依赖倒置原则也是框架设计的核心原则,善于创建可重用的框架和富有扩展性的代码,比如 Tomcat 容器的 Servlet 规范实现,Spring Ioc 容器实现。
结语
到这里,SOLID 设计原则就全部介绍完了,本文的主要目的还是对这六项原则系统地整理和总结,在后续的程序设计开发过程中能有意识地识别出设计原则和模式。如果大家对设计原则有更多想法和理解,欢迎留言,大家共同探讨。
本文由博客一文多发平台 OpenWrite 发布!
工作那么久,才知道的 SOLID 设计原则的更多相关文章
- 不得不看,只有专家才知道的17个SQL查询提速秘诀!
不得不看,只有专家才知道的17个SQL查询提速秘诀! 原创 2018-01-23 布加迪编译 51CTO技术栈 “ 除非你遵循本文介绍的这些技巧,否则很容易编写出减慢查询速度或锁死数据库的数据库代码. ...
- 每个开发者都应该知道的SOLID原则
每个开发者都应该知道的SOLID原则 单一职责原则(SRP) 它为什么违反了 SRP? 这种设计将来会带来什么问题? 开闭原则(OCP) 如何使它(AnimalSound)符合 OCP? 里氏替换原则 ...
- 适用于Java开发人员的SOLID设计原则简介
看看这篇针对Java开发人员的SOLID设计原则简介.抽丝剥茧,细说架构那些事——[优锐课] 当你刚接触软件工程时,这些原理和设计模式不容易理解或习惯.我们都遇到了问题,很难理解SOLID + DP的 ...
- SOLID设计原则
SOLID设计原则 Single Responsibility Principle单一职责原则 单一职责原则(SRP)表明一个类有且只有一个职责. 一个类就像容器一样,它能添加任意数量的属性.方法等. ...
- SOLID 设计原则 In C# 代码实现
[S] Single Responsibility Principle (单一职责原则) 认为一个对象应该仅只有一个单一的职责 namespace SingleResponsibilityPrinci ...
- SOLID 设计原则
SOLID 原则基本概念: 程序设计领域, SOLID (单一功能.开闭原则.里氏替换.接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期 引入的记忆术首字母缩略字,指代了面向对象编程和面向对象 ...
- 今天才知道的JavaScript的真实历史~[转]
JavaScript真的继承自Cmm吗?JavaScript与Java有多少关系?JavaScirpt最初的设计是怎样的?这个文章是从一个叫编程人生的网站上看到的.不知道出处在哪.在许多资料,Java ...
- 每个Web开发者都应该知道的SOLID原则
面向对象的编程并不能防止难以理解或不可维护的程序.因此,Robert C. Martin 制定了五项指导原则,使开发人员很容易创建出可读性强且可维护的程序.这五项原则被称为 S.O.L.I.D 原则. ...
- 面向对象SOLID设计原则之Open-Closed原则
首先,我们看下开放-封闭原则(Open-Closed Principle,简称OCP)的概念: 是指软件实体(类.模块.函数等)应该可以扩展,但是不可修改. 任何新功能(functionality)应 ...
随机推荐
- 温故知新-Mysql索引结构&页&聚集索引&非聚集索
文章目录 摘要 索引 索引概述 索引优势劣势 索引结构 BTREE 结构 B+TREE 结构 页 索引分类 索引语法 索引设计原则 聚触索引 & 非聚触索引 你的鼓励也是我创作的动力 Post ...
- k8s学习-pod生命周期
4.2.pod生命周期 创建一个pod的时候过程如下: 1.容器环境初始化: 2.pause执行网络.容器卷等初始化工作: 3.所有的InitC按顺序执行,每个InitC执行完后才能执行下一个,且必须 ...
- HDU-3033 I love sneakers! 题解
题目大意 有 n 个物品,分成了 k 组,每个物品有体积和价值,把 n 个物品放到容量为 V 的背包中,保证每组至少放一件,求能获得的最大价值,如果不能实现,输出"Impossible&qu ...
- 8、react 高阶组件
1.高阶组件:封装 高阶组件使用得是react得一种模式,增强现有组件得功能 一个高阶组件就是一个函数,这个函数接收得是组件类作为参数得,并且返回得是一个新组件,再返回得新组件中有输入参数组件不具备得 ...
- (四)MySQL条件查询(通配符、模糊查询)、排序查询、分组查询(单行、分组函数)
一.条件查询 1.含义:前面学的基础查询可以查询一个或多个字段,如果需要的数据仅仅是其中的某一行或多行就用到了条件查询. 2.语法:(序号表示语句执行顺序) SELECT 字段名 ③ FROM 表名 ...
- 一个老牌程序员说:做Java开发,怎么可以不会这 20 种类库和 API
- linux最小化安装命令补全
bash-completion 需要安装bash-completion才能补全,安装后,重新打开一个窗口就能生效.
- Javascript的单线程和异步编程
运行时概念 下面的内容解释了一个理论上的模型.现代 JavaScript 引擎着重实现和优化了描述的几个语义. 可视化描述 栈 函数调用形成了一个栈帧. function foo(b) { var a ...
- Jenkins入门-环境搭建(1)-转
因为Jenkins的环境搭建比较简单,本来不想来介绍,但是发现有些入门小朋友,从各种网站上下载的各种安装包来搭建,最后导致出现了各种千奇百怪的问题,介于这种情况下我决定还是来写一下Jenkins的环境 ...
- 入门大数据---Kafka的搭建与应用
前言 上一章介绍了Kafka是什么,这章就讲讲怎么搭建以及如何使用. 快速开始 Step 1:Download the code Download the 2.4.1 release and un-t ...