文章目录
六大设计原则通览
缩写 | 英文名称 | 中文名称 |
---|---|---|
SRP | Single Responsibility Principle | 单一职责原则 |
OCP | Open Close Principle | 开闭原则 |
LSP | Liskov Substitution Principle | 里氏替换原则 |
LoD | Law of Demeter ( Least Knowledge Principle) | 迪米特法则(最少知道原则) |
ISP | Interface Segregation Principle | 接口分离原则 |
DIP | Dependency Inversion Principle | 依赖倒置原则 |
单一职责原则
定义
A class should have a single responsibility, where a responsibility is nothing but a reason to change.
即:一个类只允许有一个职责,即只有一个导致该类变更的原因。
优点
- 类的复杂性降低,实现什么职责都有清晰明确的定义;
- 可读性提高;
- 可维护性提高;
- 变更引起的风险降低,耦合度降低了必然对系统的扩展性、维护性有非常大的帮助;
实践
- 单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。
- 在实际开发中,类的单一职责收到许多因素的制约,所以建议:接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。
- 举例
CALayer
:动画和视图的显示。UIView
:只负责事件传递、事件响应。
里氏替换原则
定义
所有引用基类的地方必须能透明地使用其子类的对象。
通俗点讲,只要是父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。
优点
可以检验继承使用的正确性,约束继承在使用上的泛滥。
实践
- 子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更宽松。
- KVO中间类的使用就遵从了里氏替换原则
依赖倒置原则
定义
High level modules should not depend upon low level modules. Both should depend upon
abstractions. Abstractions should not depend upon details. Details should depend upon
ab stractions.
翻译过来包含三层含义:
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象;
- 抽象不应该依赖细节;
- 细节应该依赖抽象。
定义解读
抽象就是指接口或者抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化
- 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
- 尽量不要从具体的类派生,而是以继承抽象类或实现接口来实现;
- 针对接口编程,而不是针对实现编程。
优点
- 减少类间的耦合性
- 提高系统的稳定性,降低并行开发的风险
- 提高代码的可读性和可维护性
实践
抽象是对实现的约束,对依赖者而言,也是一种契约,不仅仅约束自己,还同时约束自己与外部的关系。
依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合,我们怎么在项目中使用这个规则呢?只要遵循以下的几个规则就可以:
- 每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备(在OC中接口的体现就是协议)
- 任何类都不应该从具体类派生
- 结合里氏替换原则使用
接口分离原则
定义
多个特定的客户端接口要好于一个通用性的总接口。
定义解读
- 客户端不应该依赖它不需要实现的接口。
- 不建立庞大臃肿的接口,应尽量细化接口,接口中的方法应该尽量少。
需要注意的是:接口的粒度也不能太小。如果过小,则会造成接口数量过多,使设计复杂化。
接口分离原则与单一职责的审视角度是不同的,单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口分离原则要求接口的方法尽量少。
优点
避免同一个接口里面包含不同类职责的方法,接口责任划分更加明确,符合高内聚低耦合的思想。
实践
-
在iOS中的体现就是:使用多个专门的协议,而不是一个庞大臃肿的协议。
-
做iOS开发的朋友对
UITableView
的UITableViewDelegate
和UITableViewDataSource
这两个协议应该会非常熟悉。这两个协议里的方法都是与UITableView
相关的,但iOS SDK的设计者却把这些方法放在不同的两个协议中。原因就是这两个协议所包含的方法所处理的任务是不同的两种:UITableViewDelegate
:含有的方法是UITableView
的实例告知其代理一些点击事件的方法,即事件的传递,方向是从UITableView
的实例到其代理。UITableViewDataSource
:含有的方法是UITableView
的代理传给UITableView
一些必要数据供UITableView
展示出来,即数据的传递,方向是从UITableView
的代理到UITableView
。
很显然,
UITableView
协议的设计者很好地实践了接口分离的原则,值得我们大家学习。
迪米特法则
定义
一个对象应该对其他对象有最少的了解,也就是只了解那些真正需要了解的对象。
优点
实践迪米特法则可以良好地降低类与类之间的耦合,减少类与类之间的关联程度,让类与类之间的协作更加直接。
实践
迪米特法则的核心观念就是高内聚、低耦合。要求类“羞涩”一点,尽量不要对外公布太多的接口,将无需暴露出去的属性放在扩展中。
开闭原则
定义
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
定义的解读
- 开闭原则是最基础的一个原则,前五个原则都是开闭原则的具体形态,也就是说前五个原则就是指导设计的工具和方法,而开闭原则才是其精神领袖。
- 用抽象构建框架,用实现扩展细节。
- 不以改动原有类的方式来实现新需求,而是应该以实现事先抽象出来的接口(或具体类继承抽象类)的方式来实现。
优点
- 提高了程序的复用性
- 提高了程序的可维护性
实践
为了更好地实践开闭原则,在设计之初就要想清楚在该场景里哪些数据(或行为)是一定不变(或很难再改变)的,哪些是很容易变动的。将后者抽象成接口或抽象方法,以便于在将来通过创造具体的实现应对不同的需求。