常见的设计模式原则
经典的 solid 原则
单一职责原则(Single Responsibility Principle 简称 SRP)
定义
应该有且仅有一个原因引起类的变更(There should never be more than one reason for a class to change)
优点
类的复杂性降低,实现什么职责都有清晰明确的定义;
可读性提高,复杂性降低,那当然可读性提高了;
可维护性提高,可读性提高,那当然更容易维护了;
变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助,单一职责适用于接口、类,同时也适用于方法
缺点
关于职责的定义比较模糊,没有量化的标准
最佳实践
对于单一职责原则,建议接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。
里氏替换原则(Liskov Substitution Principle 简称 LSP)
定义
第一种定义,也是最正宗的定义:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.(如果对每一个类型为 S 的对象 o1,都有类型为 T 的对象 o2,使得以 T 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 S 是类型 T 的子类型。)
第二种定义:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.(所有引用基类的地方必须能透明地使用其子类的对象。)
即:只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应
里氏替换原则为良好的继承定义了一个规范,包括一下四点
子类必须完全实现父类的方法
子类可以有自己的个性
覆盖或实现父类的方法时输入参数可以被放大(就是输入参数的类型宽于父类的类型覆盖范围)
覆写或实现父类的方法时输出结果可以被缩小
最佳实践
在项目中,采用里氏替换原则时,尽量避免子类的“个性”,一旦子类有“个性”,这个子类和父类之间的关系就很难调和了,把子类当做父类使用,子类的“个性”被抹杀——委屈了点;把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离——缺乏类替换的标准
依赖倒置原则(Dependence Inversion Principle 简称 DIP)
每一个逻辑的实现都是由原子逻辑组成的,不可分割的 原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化
定义
高层模块不应该依赖低层模块,两者都应该依赖其抽象;
抽象不应该依赖细节;
细节应该依赖抽象。
依赖倒置原则在 PHP 语言中的表现为
模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
接口或抽象类不依赖于实现类;
实现类依赖接口或抽象类
优点
采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风 险,提高代码的可读性和可维护性
依赖的三种实现方式
构造函数传递依赖对象,在类中通过构造函数声明依赖对象,按照依赖注入的说法,这种方式叫做构造函数注入,按照这种方式的注入
Setter 方法传递依赖对象,在抽象中设置 Setter 方法声明依赖关系,依照依赖注入的说法,这是 Setter 依赖注入,按 照这种方式的注入
接口声明依赖对象,在接口的方法中声明依赖对象,该方法也 叫做接口注入。
最佳实践
每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备
变量的表面类型尽量是接口或者是抽象类
任何类都不应该从具体类派生
尽量不要覆写基类的方法(如果基类是一个抽象类,而且这个方法已经实现了,子类尽量不要覆写。类间依赖的是抽象,覆写了抽象方法,对依赖的稳定性会产生一定的影响)
结合里氏替换原则使用
接口隔离原则(Interface Segregation Principle 简称 ISP)
定义
客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上
客户端依赖它需要的接口,客户端需要什么接口就提供什么接口,把不需要的接口剔除掉,那就需要对接口进行细化,保证其纯洁性
接口隔离原则与单一职责的审视角度是不相同的,单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离原则要求接口的方法尽量少,根据接口隔离原则拆分接口时,首先必须满足单一职责原则
接口隔离原则是对接口进行规范约束,其包含以下 4 层含义:
接口要尽量小
接口要高内聚
定制服务
接口设计是有限度的
最佳实践
一个接口只服务于一个模块或业务逻辑;
通过业务逻辑压缩接口中的 public 方法;
已经被污染的接口,尽量去修改,如风险大,则采用适配器模式转化处理;
了解环境,拒绝盲从。
迪米特法则(Law of Demeter 简称 LoD)
也称为最少知识原则(Least Knowledge Principle,LKP)
一个对象应该对其他对象有最少的了解
只和朋友交流,出现在成员变量、方法的输入输出参数中的类称为朋友类,方法体内的不算。类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不引入一个类中不存在的对象。
朋友间也是有距离的,一个类公开的 public 属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。因此设计时需要反复衡量是否可以减少 public 方法或属性。
是自己的就是自己的,如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中。
开闭原则(Open Closed Principle 简称 OCP)
定义
定义:一个软件实体如类、模块、和函数应该对扩展开放,对修改关闭。
前面五个原则就是指导设计的工具和方法,而开闭原则才是其精神领袖。
重要性:
扩展操作,避免修改单元测试,及回归测试;
提高代码复用性,缩小逻辑粒度,直到一个逻辑不可再拆分为止;
提高可维护性;
面向对象开发的要求。
使用
抽象约束
通过接口或抽象类约束扩展,对扩展进行限定,不允许出现在接口或抽象类不存在的 public 方法;
参数类型、引用对象尽量使用接口或抽象类,而不是实现类;
抽象层尽量保持稳定,一旦确定不允许修改。
元数据控制模块行为,通过扩展一个子类,修改配置文件完成业务变化,如依赖注入;
制定项目章程;
封装变化,找出预计有变化或不稳定的点,为这些变化点创建稳定的接口
将相同变化封装到一个接口或抽象类中;
不应该有两个不同的变化出现在同一个接口或抽象类中
KISS 原则
定义
KISS 原则的英文描述有好几个版本,比如下面这几个。
不过,仔细看就会发现,它们要表达的意思其实差不多,翻译成中文就是:尽量保持简单。
代码的可读性和可维护性是衡量代码质量非常重要的两个标准。而 KISS 原则就是保持代码可读和可维护的重要手段。代码足够简单,也就意味着很容易读懂。
什么样的代码才算简单
代码行数越少就越“简单”吗?
并不是代码行数越少越简单,例如使用一些位运算符,>>2
,代码很简洁,但是不容易阅读
代码逻辑复杂就违背 KISS 原则吗?
例如 KMP 算法,KMP 算法以快速高效著称。当需要处理长文本字符串匹配问题(几百 MB 大小文本内 容的匹配),或者字符串匹配是某个产品的核心功能(比如 Vim、Word 等文本编辑 器),又或者字符串匹配算法是系统性能瓶颈的时候,就应该选择尽可能高效的 KMP 算法。而 KMP 算法本身具有逻辑复杂、实现难度大、可读性差的特点。本身就复杂的问题,用复杂的方法解决,并不违背 KISS 原则。
如何写出满足 KISS 原则的代码
不要使用同事可能不懂的技术来实现代码。比如前面例子中的正则表达式,还有一些编程语言中过于高级的语法等。
不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出 bug 的概率会更高,维护的成本也比较高。
不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替 if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。
小节
KISS 侧重点是提高代码的可读性与*可维护性*,所以使代码尽量简单。角度不同,自己认为还是要与团队成员一起约定好,一起进步更佳。
YAGNI 原则
YAGNI 原则的英文全称是:You Ain’t Gonna Need It。直译就是:你不会需要它。这条原则也算是万金油了。当用在软件开发中的时候,它的意思是:不要去设计当前用不到的功能;不要去编写当前用不到的代码。实际上,这条原则的核心思想就是:不要做过度设计。
YAGNI 原则跟 KISS 原则并非一回事儿。KISS 原则讲的是“如何做”的问题(尽量保持简单),而 YAGNI 原则说的是“要不要做”的问题(当前不需要的就不要做)。
版权声明: 本文为 InfoQ 作者【一个大红包】的原创文章。
原文链接:【http://xie.infoq.cn/article/b5a22de215a5b1cd45bdeec3f】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论