因为之前对于这些概念了解很少,在实际工作中遇到了很多的坑,借着内部交流的机会也重新学习了一下。
事实证明,我们当初说这些概念是shit的时候是有多愚钝。
内容基本是把书上的东西自己写了一遍,比较简单。
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
- 前言--面向对象五原则
- 单一职责原则:?
- 开放封闭原则:对扩展开放,对修改封闭
- 里氏替换原则:引用基类的地方必须可以使用其子类的对象代替
- 依赖倒置原则:模块依赖接口,细节依赖抽象
- 接口隔离原则:多个专门接口,而不是一个总接口
-
单一职责原则的概念
- 内聚性:一个模块的组成元素之间的功能相关性
- 将内聚性和引起一个模块或者类的作用力相联系
- 就一个类而言,应该仅有一个引起它变化的原因,职责即变化的原因
-
单一职责的原因
- 因为每一个职责都是变化的轴线。
- 当需求变化时,该变化会反映为类的职责的变化。
- 一个类承担了多余一个的职责,那么引起它变化的原因就会有多个。
- 职责的耦合导致设计的脆弱。
-
举例1
- 违反单一职责的情况:
Rectangle类具有两个职责:计算和绘图。 如果有两个程序(绘图程序、计算程序)分别依赖这个类,导致的问题:- 计算程序中需要包含gui代码;
- 绘图需求的改变会导致类的改变,这时由于内在的耦合,计算程序也是需要重新测试和构建的。
- 合适的改造:
将Rectangle类中的计算部分迁移到Geometric Rectangle类中,实现两个完全不同的类来保证单一职责。
- 违反单一职责的情况:
-
举例2
- 违背单一职责的接口
interface Modem { public void dial(String pon); public void hangup(); public void send(char c); public void recv(); }
此时的接口有两个职责:
- 链接管理:dial(), hangup();
- 数据通信:send(), recv();
是否应该被分开: 变化的轴线仅当实际发生时才有意义,否则就会有不必要的复杂性。
- 应用程序的变化方式总是导致这两个职责同时变化,那么就不必分离;
- 反之,这两个职责应该被分离,如下图。
-
分离耦合
上图中,通过接口将耦合的职责分开,但实际上ModemImplementation也应该被分离,但有时这些分离是困难甚至是难以实现的,这个时候应当保证所有的依赖关系与它无关。大家只需要知道接口就可以,后续的依赖倒置原则会有更深入的讲解。 -
持久化
业务规则与持久化的分离是必要的。因为业务规则总是易变的,而数据持久化的方式很少发生改变。 -
结论
单一职责听起来很简单,做起来却很难,因为我们总是自然而然地倾向与把职责结合在一起(习惯于面向过程的思维)。 -
扩展
不仅仅是类,方法,函数,甚至是变量也需要做到职责的分离,例如:你不应当使用一个变量即代表正确返回值,又代表错误时的错误原因。var result; if (isOk) { result = [1,2,3]; } else { result = 'error'; } var result = { data: [], error: null }; if (isOk) { result.data = [1,2,3]; } else { result.error = 'error'; }