六大原则不熟?那你学什么设计模式?来来来,赶紧来!
单一职责原则
什么是“单一职责原则”?
如果要理解为:一个类只有一个职责,当然也是可以的,简单化嘛。 单一职责的原话解释是这样的:There should never be more than one reason for a class to change. 什么意思?那里,应该,没有,多于,一个,原因,使得,类,去,改变。 啊,咱这蹩脚英语,勉强能翻译啊。
不过,能看懂是一回事,具体实现就是另一个故事了。。。
饱受争议的原则
为什么饱受争议呢?看着多单纯一原则啊。 这样,我们来看一个打电话的过程:
class 电话{ public: void 拨出电话(string 电话号码); void 瞎比比(Object *哔哔类对象); //总不能传个string吧,说一句就没啦? void 挂电话(); };
这个有没有问题?是有那么小问题的嘞。 你说我哪天,拨号的方法要改一下,我变成拨不通就一直拨,那这个类变一下。 然后我通信的方法再改一下,我现在不允许两个人同时说话,一个说完另一个再说,那这个类再变一下。
这个类,有两个职责:协议管理和数据传送。
那怎么搞?把那俩接口独立出来呗,然后将两个职责融合在一个类中。
现在变成了一个类融合了两个接口,确实那个实现类还是有两个原因引起变化,但是别忘了,我们是面向接口编程(后面会提到,依赖倒置原则)。我们对外公布的是接口,又不是实现类。
如果你非要对这个栗子实现单一原则,也可以,你要有那个权力或精力(因为我估计没人愿意陪你这样玩)。
“单一职责原则”的优势
- 类的复杂性降低,实现什么职责都有明确的定义。 这是最首要的,如果不能降低复杂度,那就别分开
- 可读性提高
- 可维护性提高
- 变更引起的风险降低
怎么用?自己看着办
对于接口,我们在实现的时候一定要做到单一,但是对于实现类就需要多方面考虑了。 生搬硬套的话会有什么不良反应,去试试就知道了。
单一职责很难在项目中得到体现,就拿上面那栗子来说,能把接口分开就谢天谢地吧。 当然,单一职责也可以用于类方法,说来惭愧,我曾经用一个类方法实现五个功能(通过巧妙设置参数)。。。。 现在想想,真是好笑啊。
里氏替换原则
什么是“里氏替换原则”?
这个原则,说简单也简单,说拗口也拗口。
是这样说的:Function that use pointers or references to base classes must be able to use objects of derived classes without knowing it. 所有引用基类的地方,必须能透明的使用其子类对象。
什么意思呢?就是子类必须实现父类的所有方法。有父类出现的地方,子类就可以出现。
关于里氏替换原则
关于里氏替换原则,我并不想讲太多,无非就是父类弄成纯虚基类,然后客端调用的时候以子类来new出父类声明的对象:父类 * 对象 = new 某子类();
这种格式后面会常见,见到的时候自然就明白了。
依赖倒置原则
什么是“依赖倒置原则”
这是我最喜欢的一个原则,也是受益最大的。 它的定义是:High level modules should not depend upon low level modules. Both should depend upon abstractions. Absteactions should not depend upon details.Details should depend upon abstractions.
- 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
- 抽象不应该依赖于细节。
- 细节应该依赖于抽象。
推荐先看我之前写的小故事:依赖倒置
依赖倒置,让项目并驾齐驱
我们来思考一下依赖倒置对并行开发的影响。 如果两个类之间有依赖关系,只要定制出两者之间的接口(或抽象类),就可以独立开发了。就像我最近做的一个图书管理项目,只要合理地运用依赖倒置,便可以很好的将界面与后台数据访问解耦合,从而实现并行开发。
最佳实践
依赖倒置原则的本质就是通过抽象使得各个类或模块的实现彼此独立,不互相影响,实现模块之间的松耦合,我们怎么在项目中使用这个规则呢?只要通过以下的几个规则:
- 每个类尽量都有接口或抽象类,或者抽象和接口二者都具备。
- 变量的表面类型尽量是接口或者抽象类。
- 任何类都不应该从具体类派生。
- 尽量不要覆写基类的方法。
- 结合里氏替换原则。
反正我以前是一条都没对上。
“依赖倒转原则”在小项目上很难体现出来。
接口隔离原则
什么是“接口隔离原则”?
它建立在“依赖倒置原则”之上, 它的定义有俩:
- Client should not be forced to depend upon interfaces that they don’t use.
- 客户端不应该依赖于它不需要的接口。
- The dependency of one class to another one should depend on the smallest possible interface.
- 类之间的依赖关系应该建立在最小的接口上。
简单易懂啊,如果对前面那个原则有一定的把握。
建立单一接口,接口尽量细化。
接口要高内聚
什么是高内聚?高内聚就是提高接口、类、模块的处理能力,减少对外的交互。比如说你告诉你的保镖,今天去给我买一打爱马仕,他就去了。你也不用问他花了多少钱,他也不用问你是不是抽风了,这种不问条件执行的行为就是高内聚。 接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也越大,同时也有利于降低成本。
最佳实践
- 一个接口只服务于一个子模块或业务逻辑 - 通过业务逻辑压缩接口中的public方法,接口要勤快点重构 - 已经被污染的接口,尽量去修改 - 了解环境,拒绝盲从
迪米特法则
松内聚的法则:迪米特法则
英文解释:Only talk to your immedate friends. 只与直接的朋友通信。
如果两个类之间不能直接通信,那么这两个类就不应该发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法到底话,可以通过第三者转发这个调用。
迪米特法则首先强调在类的设计上,每一个类都应该尽量降低成员的访问权限,强调了类之间的松耦合。
类之间的耦合越弱,越有利于重复利用,一个处在弱耦合的类被修改,不会对相关类造成波及。
开-闭原则
何为“开闭原则”
Software entities like classes, modules and functions should be open for extension but closed for modifications. 一个软件实体,应该对拓展开放,对修改关闭。
抽象实体:
项目或软件产品中按照一定的逻辑规则划分的模块。 抽象类 方法
这个原则很重要,后面会很经常见。 多说无益,我就稍微说两句,后面慢慢看它真面目。
如何应对需求变化?
既然说,对修改要关闭,那需求变化了怎么办? 有如下方法:
1、修改接口 2、修改实现类 3、通过拓展实现变化
至于为什么需要这个原则、如何使用、何时使用这个原则,跟着我的步伐,往后看。
今天的分享到此告一段落,算是我回归设计模式模块的礼物。
- 重温Delphi之:面向对象
- Android新手之旅(15) Win7下配置遇到的问题
- 重温Delphi之:如何定义一个类
- Android新手之旅(2) 新手问题
- Android新手之旅(2) 新手问题
- Android新手之旅(9) 自定义的折线图
- 2018春节抢票攻略:不仅仅是12306微信小程序启用
- Android新手之旅(9) 自定义的折线图
- Android新手之旅(11) 在现有页面中插入新的view
- Docker容器学习梳理--容器间网络通信设置(Pipework和Open vSwitch)
- 温故而知新:Asp.Net中如何正确使用Session
- Android新手之旅(13) listview中数据重复的问题
- 温故而知新:HttpApplication,HttpModule,HttpContext及Asp.Net页生命周期
- proxy_pass反向代理配置中url后面加不加/的说明
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android开发第六讲EditText 编辑框
- Android 开发第七讲 RadioButton (单选按钮)
- linux内核写时复制机制源代码解读
- akka-grpc - 应用案例
- Python从入门到大师教程 | 二、搭建Jupyter Notebook环境
- 收益3583万?我是如何快速统计「李子柒」YouTube频道视频累计播放量并计算收益的
- mysql优化篇:where中的like和=的性能分析
- 557. 反转字符串中的单词 III
- 剑指 Offer 03. 数组中重复的数字
- 841. 钥匙和房间
- 一篇文章了解python常见内置异常报错
- 五分钟极速搭建kubernetes集群
- 没想到吧!关于Dubbo的『消费端线程池模型』官网也写错了。
- Go内存管理之代码的逃逸分析
- MySQL读锁的区别和应用场景分析