设计是什么:
正如Kent Beck
所说,软件设计是为了「长期」更加容易地适应未来的变化。正确的软件设计方法是为了长期地、更好更快、更容易地实现软件价值的交付。
Design is there to enable you to keep changing the software easily in the long term. -- Kent Beck.
软件设计的目标:
软件设计就是为了完成如下目标,其可验证性、重要程度依次减低。
- 实现功能(需求)
- 易于重用
- 易于理解
- 没有冗余
由Kent Beck大师提出简单设计四原则一一对应软件设计的目标,我们可以根据这些原则来指导开发,并及时验证开发的结果。
实现功能
实现功能的目标压倒一切,这也是软件设计的首要标准。如何判定系统功能的完备性呢?通过所有测试用例!
从TDD
的角度看,测试用例就是对需求的阐述,是一个闭环的反馈系统,保证其系统的正确性;及其保证设计的合理性,恰如其分,不多不少;当然也是理解系统行为最重要的依据。
易于重用
易于重用的软件结构,使得其应对变化更具弹性;可被容易地修改,具有更加适应变化的能力。
最理想的情况下,所有的软件修改都具有局部性。但现实并非如此,软件设计往往需要花费很大的精力用于依赖的管理,让组件之间的关系变得清晰、一致、漂亮。
那么软件设计的最高准则是什么呢?「高内聚、低耦合」原则是提高可重用性的最高原则。为了实现高内聚,低耦合的软件设计,袁英杰提出了「正交设计」的方法论。
易于理解
好的设计不只是指导计算机执行指令,更重要的是能让其他人也能容易地理解,包括系统的行为,业务的规则。那么,什么样的设计才算得上易于理解的呢?
- Clean Code(代码整洁)
这个包含很多,如格式、封装等等,可以参阅鲍勃大叔的《代码整洁之道》
- Implement Patterns(实现模式)
采用通用的实现模式,特别是一个组织或开发团队。如DDD、架构模式、DCI、设计模式等等,是开发人员形成的共识。
- Idioms
遵循一些设计原则,如简单设计原则、正交设计原则、面向对象设计原则、高内聚低耦合、小类大对象等等原则。
没有冗余
没有冗余的系统是最简单的系统,恰如其分的系统,不做任何过度设计的系统。
- Dead Code
从未被执行的代码
- YAGNI: You Ain’t Gonna Need It
不要做过度设计:你没必要那么着急,不要给你定义的类实现太过于早的功能,只需要先实现好现在需要的功能点。
- KISS: Keep it Simple, Stupid
尽量保持简单
正交设计
「正交」是一个数学概念:所谓正交,就是指两个向量的内积为零。简单的说,就是这两个向量是垂直的。在一个正交系统里,沿着一个方向的变化,其另外一个方向不会发生变化。为此,Bob大叔将「职责」定义为「变化的原因」。
「正交性」,意味着更高的内聚,更低的耦合。为此,正交性可以用于衡量系统的可重用性。那么,如何保证设计的正交性呢?袁英杰提出了「正交设计的四个基本原则」,简明扼要,道破了软件设计的精髓所在。
正交设计原则
- 消除重复
- 分离关注点
- 缩小依赖范围
- 向稳定的方向依赖
消除重复
什么是重复代码?
重复是不同的代码元素对同一知识的多次描述,无论描述方式是否一致。
完全重复 | 删掉重复 |
参数型重复:两个函数的算法相同,只是处理的数据不同 | 将差异的数据参数化 |
调用型重复 | 如果两个函数的重复部分完全相同,可以将重复的部分提取为函数F,然后原函数各自对F进行调用 |
回调型重复 | 如果两个函数的重复部分完全相同,可以将重复的部分提取为函数F,将差异的部分形成原型相同的两个函数s1、s2,然后通过F分别对s1、s2进行调用 |
消除重复不影响原有功能的重用粒度。
重复产生的常见原因:
- 低成本(拷贝粘贴)
- 对于变化的恐惧(另起炉灶)
- 不易识别(代码混乱)
- 价值导向(不重视内部质量)
- 认知差异(不具备敏锐的洞察力)
重复和重用
- 消除重复的过程就是一个提高系统可重用性的过程;
- 提高系统的可重用性,也就降低了未来产生重复的可能;
- DRY(Don't Repeat Youself)
- OAOO(Once And Only Once)
- SRP(Singular Responsibility Principle)
分离关注点
所谓关注点,从行为的角度看是一个个功能,即在某个层面,你可以清晰的描述它在做一件什么具体的事情。
缩小依赖范围
如何缩小依赖范围:
- 尽可能减少依赖点的数量;
- 依赖点应包含尽可能少的知识;
- 依赖点也应该高内聚,而不应该强迫依赖方依赖它不需要的东西;
向着稳定的方向依赖
依赖点越稳定,依赖方受依赖点变化影响的概率越低。
如何让依赖更趋于稳定:
- 站在需求的角度,而不是实现的角度定义依赖点(API),会让API更加稳定。
- 需求是不断变化的,必须对需求进行抽象和建模,找出其中本质的东西,才能使API更加稳定。