写点什么

常见的设计模式原则

发布于: 2021 年 03 月 06 日
常见的设计模式原则

经典的 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.(所有引用基类的地方必须能透明地使用其子类的对象。)


即:只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应


里氏替换原则为良好的继承定义了一个规范,包括一下四点


  1. 子类必须完全实现父类的方法

  2. 子类可以有自己的个性

  3. 覆盖或实现父类的方法时输入参数可以被放大(就是输入参数的类型宽于父类的类型覆盖范围)

  4. 覆写或实现父类的方法时输出结果可以被缩小

最佳实践

在项目中,采用里氏替换原则时,尽量避免子类的“个性”,一旦子类有“个性”,这个子类和父类之间的关系就很难调和了,把子类当做父类使用,子类的“个性”被抹杀——委屈了点;把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离——缺乏类替换的标准

依赖倒置原则(Dependence Inversion Principle 简称 DIP)


每一个逻辑的实现都是由原子逻辑组成的,不可分割的 原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化

定义


  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;

  • 抽象不应该依赖细节;

  • 细节应该依赖抽象。

依赖倒置原则在 PHP 语言中的表现为

  • 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;

  • 接口或抽象类不依赖于实现类;

  • 实现类依赖接口或抽象类

优点

采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风 险,提高代码的可读性和可维护性

依赖的三种实现方式

  1. 构造函数传递依赖对象,在类中通过构造函数声明依赖对象,按照依赖注入的说法,这种方式叫做构造函数注入,按照这种方式的注入

  2. Setter 方法传递依赖对象,在抽象中设置 Setter 方法声明依赖关系,依照依赖注入的说法,这是 Setter 依赖注入,按 照这种方式的注入

  3. 接口声明依赖对象,在接口的方法中声明依赖对象,该方法也 叫做接口注入。

最佳实践

  1. 每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备

  2. 变量的表面类型尽量是接口或者是抽象类

  3. 任何类都不应该从具体类派生

  4. 尽量不要覆写基类的方法(如果基类是一个抽象类,而且这个方法已经实现了,子类尽量不要覆写。类间依赖的是抽象,覆写了抽象方法,对依赖的稳定性会产生一定的影响)

  5. 结合里氏替换原则使用

接口隔离原则(Interface Segregation Principle 简称 ISP)

定义

客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上

客户端依赖它需要的接口,客户端需要什么接口就提供什么接口,把不需要的接口剔除掉,那就需要对接口进行细化,保证其纯洁性

接口隔离原则与单一职责的审视角度是不相同的,单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离原则要求接口的方法尽量少,根据接口隔离原则拆分接口时,首先必须满足单一职责原则

接口隔离原则是对接口进行规范约束,其包含以下 4 层含义:

  1. 接口要尽量小

  2. 接口要高内聚

  3. 定制服务

  4. 接口设计是有限度的

最佳实践

一个接口只服务于一个模块或业务逻辑;

通过业务逻辑压缩接口中的 public 方法;

已经被污染的接口,尽量去修改,如风险大,则采用适配器模式转化处理;

了解环境,拒绝盲从。

迪米特法则(Law of Demeter 简称 LoD)

也称为最少知识原则(Least Knowledge Principle,LKP)

一个对象应该对其他对象有最少的了解

只和朋友交流,出现在成员变量、方法的输入输出参数中的类称为朋友类,方法体内的不算。类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不引入一个类中不存在的对象。

朋友间也是有距离的,一个类公开的 public 属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。因此设计时需要反复衡量是否可以减少 public 方法或属性。

是自己的就是自己的,如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中。

开闭原则(Open Closed Principle 简称 OCP)

定义

定义:一个软件实体如类、模块、和函数应该对扩展开放,对修改关闭。

前面五个原则就是指导设计的工具和方法,而开闭原则才是其精神领袖。

重要性:

扩展操作,避免修改单元测试,及回归测试;

提高代码复用性,缩小逻辑粒度,直到一个逻辑不可再拆分为止;

提高可维护性;

面向对象开发的要求。

使用

抽象约束

通过接口或抽象类约束扩展,对扩展进行限定,不允许出现在接口或抽象类不存在的 public 方法;

参数类型、引用对象尽量使用接口或抽象类,而不是实现类;

抽象层尽量保持稳定,一旦确定不允许修改。

元数据控制模块行为,通过扩展一个子类,修改配置文件完成业务变化,如依赖注入;

制定项目章程;

封装变化,找出预计有变化或不稳定的点,为这些变化点创建稳定的接口

将相同变化封装到一个接口或抽象类中;

不应该有两个不同的变化出现在同一个接口或抽象类中

KISS 原则

定义

KISS 原则的英文描述有好几个版本,比如下面这几个。

Keep It Simple and Stupid.Keep It Short and Simple.Keep It Simple and Straightforward.
复制代码

不过,仔细看就会发现,它们要表达的意思其实差不多,翻译成中文就是:尽量保持简单


代码的可读性和可维护性是衡量代码质量非常重要的两个标准。而 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 原则说的是“要不要做”的问题(当前不需要的就不要做)。


发布于: 2021 年 03 月 06 日阅读数: 16
用户头像

多读书多看报,少吃零食多睡觉 2018.08.07 加入

还未添加个人简介

评论

发布
暂无评论
常见的设计模式原则