敏捷

敏捷软件开发(原则、模式与实践)第二部分 敏捷设计-读书笔记

2020-10-25  本文已影响0人  漫行者曰

在敏捷团队中,全局视图和软件一起演化。在每次迭代中,团队改进系统设计,使设计尽可能适合于当前系统。团队不会花费更多时间去预测未来的需求和需要。也不构建一些基础结构去支撑可能明天才回需要的特性。他们更关注当前的系统结构,并使它尽可能地好。

拙劣设计的症状

原则

设计中的臭味是可以主观进行度量的。是违反了原则中的一个或多个而导致的。敏捷团队应该应用这些原则来除去臭味。当没有臭味时,不应用这些原则。

第七章 什么是敏捷设计

本章作者提出了“源代码就是设计”的观点。接下来逐条论述了软件中的7条设计臭味:

1. 僵化性

僵化性:指难以对软件进行改动过,即使是简单的改动。如果单一的改动晦导致有依赖关系的模块中的连锁改动,那设计就是僵化的。必须要改进的模块越多,设计就越僵化。

2. 脆弱性

脆弱性:在进行一个改动时,程序的许多地方就可能出现问题。常常是,出现新问题的地方与改动的地方并没有概念上的关联。要修正这些问题就又会引出更多的问题。

3. 牢固性

牢固性:设计中包含了对其他系统有用的部分,但是要把这些部分从系统中分离出来所需要的努力和风险是巨大的。

4. 粘滞性

粘滞性有两种表现:软件的粘滞性和环境的粘滞性:
当面临一个改动时,常常发现会有多种改动的方法。其中,一些方法晦保持设计;而另外一些破坏设计。

5. 不必要的复杂性

设计中包含有当前没用的组成部分,含有不必要的复杂性。

6. 不必要的重复

当同样的代码以稍微不同的形式一再出现时,就表示开发人员忽视了抽象。

7. 晦涩性

晦涩性:指模块难以理解。

总之,敏捷团队依靠变化来获取活力。团队几乎不进行预先(Up-front)设计,不需要预先设计一个成熟的初始设计。要保持系统设计尽可能的干净、简单,并使用许多单元测试和验收测试作为支援。

敏捷开发人员致力于保持设计尽可能地适当、干净。
什么是敏捷设计?
敏捷设计是一个过程,不是一个时间。它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性的过程。
敏捷开发人员不会对一个庞大的预先设计应用那些原则和模式。这些原则和模式被应用在一次次的迭代中,力图使代码以及代码所表达的设计保持干净。

第八章 单一职责原则(SRP)

如果一个类承担的职责过多,等于把这些职责耦合在一起。耦合会导致脆弱的设计。
在SRP中,把职责定义为“变化的原因(A reason for change)”,当一个类同时包含了业务规则和对持久化的控制时,应使职责分离,应使用** Facade Proxy** 模式对设计进行重构,分离这两个职责。

第九章 开放-封闭原则(OCP)

OCP的主要机制 - ** 抽象和多态 。在JAVA中,支持抽象和多态的关键机制是 继承 **。用继承,创建实现其基类中抽象方法的派生类。

软件实体(类、模块、函数等等)应该是可以扩展的,但是不可修改的。
如果设计具有僵化性的臭味。OCP建议应对系统进行重构。

遵循开放-封闭原则设计出的模块具有两个主要的特征:

关键是抽象
模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体。通过从这个抽象体派生,也可以扩张此模块的行为。
STRATEGY模式:即开放又封闭的类。
Template Method模式:即开放又封闭的基类。
通过Strategy,Template Method两个模式是满足OCP的最常用的方法。把一个功能的通用部分和实现细节部分清晰的分离开来。

无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。设计人员必须对于他设计的模块应该对哪种编程封闭做出选择。需先猜测最有可能发生的变化种类,然后构造抽象来隔离那些变化。
抽象会增加了软件设计的复杂性。

封闭是建立在抽象基础上的。

第十章 Liskov替代原则(LSP)

对于LSP做如下解释:子类型必须能够替代掉它们的基类型。

对于违反LSP指 - 导致以明显违反OCP的方式使用运行时类型辨别(RTTI)。
继承是IS-A(“是一个”)关系。
如果新派生类的创建会导致改变基类,这意味着设计是有缺陷的,违反了OCP。
LSP:一个模型,如果孤立地看,并不具有真正意义上的有效性。模块的有效性只能通过它的客户程序来表现。
IS-A:是关于行为方式而言的,行为方式是可以进行合理假设的,是客户程序所依赖的。
基于契约设计(Design By Contract, DBC):使合理的假设明确化。使用DBC,类的编写者显示地针对该类的契约。契约是通过为每个方法声明的前置条件(Preconditions)和后置条件(Postconditions)来制定的。要一个方法得以执行,前置条件必须为真。执行完毕后,该方法保证后置条件为真。

派生类的前置条件和后置条件规则是:
在重新声明派生类中的例程(Routine)时,只能使用相等或更弱的前置条件来替代原始的前置条件,只能使用相等或更强的后置条件来替代原始的后置条件。
派生类必须和基类的所有后置条件一致。

另一个种LSP的违规形式:在派生类的方法中添加了其基类不会跑出的异常。

第十一章 依赖倒置原则(DIP)

高层业务规则的模块应优先并独立于包含实现细节的模块。

层次化
结构良好的架构都具有清晰的层次定义,每个层次通过一个定义良好的、受控的接口向外提供了一组内聚的服务。
每个较高层次都为它所需要的服务声明一个抽象接口,较低的层次实现了这些抽象接口,每个高层类都通过该抽象接口使用下一层,这样高层就不依赖于低层。低层反而依赖于在高层中声明的抽象服务接口。

倒置不仅仅是依赖关系的倒置,也是接口所有权的倒置。应用了DIP时,往往客户拥有抽象接口,服务者则从这些抽象接口派生。
不应该依赖于具体类,程序中所有的依赖关系都应该终止于抽象类或接口。

根据启发式规则:

第十二章 接口隔离原则(ISP)

通过ISP来处理“胖(Fat)”接口所具有的缺点。
如果接口不是内聚的(Cohensive),就表示该类具有“胖”的接口。这种接口类可分解成多组方法,每一组方法都服务于一组不同的客户程序。

胖类:会导致它们的客户程序之间产生不正常的并且有害的耦合关系。
通过把胖类的接口分解为多个特定于客户程序的接口,可以实现ISP的这个目标。

ISP:不应该强迫客户依赖他们不用的方法。
一个对象的客户不是必须通过该对象的接口去访问它,也可以通过委托或通过该对象的基类去访问它。
可以使用** 多重继承 ** 来达到符合ISP的目标。

上一篇 下一篇

猜你喜欢

热点阅读