如何理解设计模式的六大基本原则
设计模式是一套理论,由软件界的先辈们总结出的一套可以反复使用的经验,它可以提高代码的可重用性,增强系统的可维护性,以及解决一系列的复杂问题。在做软件的时候,需求是最难把握的,我们可以分析现有的需求,预测可能发生的变更,但是我们不能控制需求的变更。既然需求的变更是不可控的,那如何拥抱变化呢?幸运的是,设计模式给了我们指导,出现了 6 大设计原则。
这六大设计原则分别是:
- 单一职责原则 (Single Responsibility Principle,简称SRP )
- 里氏替换原则 (Liskov Substitution Principle,简称LSP)
- 依赖导致原则 (Dependence Inversion Principle,简称DIP)
- 接口隔离原则 (Interface Segregation Principle,简称ISP)
- 迪米特原则 (Law of Demeter,简称LoD)
- 开闭原则 (Open Close Principle,简称OCP)
单一职责原则
这是一个备受争议的原则,原因在于实际项目中使用这一原则的时候,需要考虑很多因素,如:项目工期、成本、人员技术水平、硬件情况、网络情况、甚至有时候还要考虑政府政策、垄断协议等因素。
从理论上讲,单一职责原则是要保证类的专一性,它的原话是这样的
There should never be more than one reason for a class to change.
从字面上讲就是「对于一个类的变化来说,绝对没有超过一个因素」。而实际上单一职责原则要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情。
同样的,方法也遵循单一职责原则,比如在修改用户信息的时候,可以设置多个方法区分别修改用户的多个信息,而不是在一个方法修改多个用户信息。
单一职责原则的好处是:
- 类的复杂性降低;
- 可读性提高;
- 可维护性提高;
- 变更引起的风险降低;
最后,对于单一职责原则的建议是接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。
里氏替换原则
里氏替换原则比较通俗易懂的定义是:
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
所有引用基类的地方必须能透明地使用其子类的对象。
这里一共包含 4 层意思:
- 子类必须完全实现父类的方法。如果子类不能完全实现父类的方法,需要脱离这一父类,委托其他父类实现
- 子类可以有自己的个性。因此,子类出现的地方,父类未必可以出现。
- 覆盖或实现父类的方法时输入参数可以被放大。这句话我觉得描述的不够准确,因此我换了一种说法,如果输入参数相同就是覆盖,输入参数可以比父类输入参数范围要大,输入参数绝对不能比父类的输入参数还要小(PS:感觉啰嗦了,但思路很清晰)
- 覆写或实现父类的方式时输出结构可以被缩小。(原理同上)
就我个人看来,这是一个防止程序在扩展时出现错乱的原则。
依赖倒置原则
这一原则先来陈述什么是倒置:正常人的思维方式是类间的依赖是实现类中的依赖关系,人开奔驰车,那么人就直接依赖奔驰车,而程序是对现实世界的抽象,而程序中为了便于适应多变的需求,多数都是依赖于抽象,这很违背人类的思维方式,因此,此为倒置。
那什么是依赖于抽象呢?就拿刚才说的人开奔驰车来说,可以用下面的类定义
|
|
那么人就只能开奔驰车了。如果需求变化,比如人开宝马车,我们就需要修改 Person 类的方法,这样的程序扩展性比较低,于是程序需要依赖于抽象,
|
|
这样的话,在变化出其他车来也可以让程序得心应手。
还有一点需要注意的是,依赖倒置原则在大型项目中优势突出,在一些小型项目中效果不明显,甚至还会造成程序变得复杂的现象。
接口隔离原则
这里有两个定义:
- 客户端不应该依赖它不需要的接口
- 类间的依赖关系应该建立在最小的接口上
接口隔离原则主要是对接口定义,同时也是对类定义,接口和类尽量使用原子接口或原子类类组装。但是,这个原子的发奋是设计模式中一大难题,在时间中可以根据以下几个规则类衡量:
- 一个接口只服务于一个子模块或业务逻辑;
- 通过业务逻辑压缩接口中 public 方法,避免接口臃肿;
- 已经被污染的接口,尽量去修改,若变更的风险较大,则采用适配器模式转化处理;
- 了解环境,拒绝盲从。看到某大神的案例,就照本宣科的照抄。这样做很危险,环境不同,接口拆分的标准就不同。深入了解业务逻辑,才能设计出最好的接口;
接口隔离原则要掌握一个「度」,接口粒度小,会增加系统的灵活度,但会增加系统的开发难度,造成提高开发成本;接口粒度大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
那如何准确地时间接口隔离原则呢?时间、经验和领悟!
迪米特原则
迪米特原则的核心观点就是类间解耦,弱解耦,只有弱解耦以后,类的复用率才可以提高。其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度。读者采用迪米特原则是需要反复权衡,既做到让结构清晰,又做到高内聚低耦合。
迪米特原则要求类间解耦,但解耦是有限度的,除非是计算机的最小单元——二进制的 0 和 1。那才是完全解耦,在实际项目中,需要适度地考虑这个原则,别为了套用原则而做项目。原则知识工参考,如果违背了这个原则,项目也未必会失败,这就需要大家在采用原则时反复度量,不遵循是不对的,严格执行就是“过犹不及”。
开闭原则
在 6 个原则中,开闭原则是重中之重,是最基本的原则,是其他 5 个原则的精神领袖。我们在使用开闭原则时要注意一下几个问题:
- 开闭原则也只是一个原则(不要过度依赖)
- 项目章程非常重要
- 预知变化
在项目中设计模式的使用
我在工作中使用过的设计模式是单例模式和观察者模式。
我将数据类定义成单例模式,为了减少数据的传递,将数据存储在单一实例中,避免了数据的丢失,而且能够相应数据更新
在项目中有一个业务场景,向串口写出指令,之后串口会有数据返回,在数据返回后,个业务层会去集合中寻找数据,这样的话会导致有时数据获取不正确或获取不到数据的问题。因此我采用观察者模式,当数据到来以后,通知所有业务层,确保可以让业务层即时拿到数据。