《敏捷软件开发》学习笔记:敏捷设计原则
2011-2-15
遵循敏捷实践去发现问题;应用设计原则去诊断问题;应用适当的设计模式去解决问题。
软件开发这三个方面件的相互作用就是设计。
就一个类而言,应该仅有一个引起它变化的原因。
把职责定义为“变化的原因”。如果有多于一个动机去改变一个类,这个类就具有多于一个的职责。
OCP 是面向对象设计的核心所在。
Open for extension, Closed for modification.
如何实现?
实现这个原则的关键是抽象。模块依赖于一个固定的抽象体,它对于更改是关闭的。通过从这个抽象体派生,可以扩展此模块的行为。
判断出可能发生的变化,然后构造抽象隔离这些变化。
常见的模式:stategy, template method。
怎么知道变化在那里?
1) 根据经验判断;
2) 等待;
主动的刺激变化发生,可以构建测试用例,缩短交付周期,先交付重要的特性,尽早交付。这就是敏捷开发的好处之一。
抽象不能滥用
应该仅仅对频繁变化的那些部分做出抽象。
LSP:子类型(subtype)必须能够替换它们的基类型(base type)。
OCP 是OOD 中很多说法的核心。LSP 是使 OCP成为可能的主要原则之一。
子类型的可替换性,使得使用基类类型的模块在无需修改的情况下就可以扩展。
可替换性并非可以轻易获得,依赖于客户的合理假设
IS-A的含义过于宽泛,不能用来判断可替换性。IS-A关系是就行为方式而言的,行为方式是可以进行合理假设的,是客户程序所依赖的。
一个模型,如果孤立的看,并不具有真正意义上的有效性。模型的有效性只能通过它的客户程序来表现。必须根据使用者的合理假设进行审视。
基于契约的编程,可以使合理的假设明确化
契约是通过为每个方法声明的前置条件和后置条件来制定的。
可替换性要求:子类型的前置条件不能更强,后置条件不能更弱。
修改设计,符合LSP
如果IS-A关系不能符合LSP,需要进行设计的修改:提取公共部分作为基类。
违反LSP 的用法
1)派生类中的退化函数;
2)从派生类中抛出异常;
传统的过程化设计方法,会产生层次化的结构,上层对底层产生依赖。
而面向对象设计中,我们可以将依赖关系倒置,高层模块不应该依赖于底层模块,二者都应该依赖于抽象。
抽象接口的所有权属于高层(所以变化较少)。抽象不依赖于细节,底层反而依赖于在高层中声明的抽象服务接口,这就是倒置的含义。这和传统的过程化程序设计所创建出来的依赖关系结构相反。
依赖终止于抽象
程序中所有的依赖都应该终止于抽象类或者接口。把不稳定隐藏在抽象后面,隔离变化。
编程要求:
1)变量不能持有具体类对象的指针、引用;
2)不能从具体类继承;
3)不能覆盖基类中已经实现的方法;
两种实现方法
1) 动态
2) 静态 -- 使用模板
设计中要避免Fat接口,因为:
1)客户程序会依赖它们不用的接口;
2)客户程序会对接口产生反作用力,引起接口的变化;
3)如果客户程序是分离的,接口也应该保持分离。否则,不同客户之间会产生依赖。
存在的约束:有时出于实现的需要,一些无关的接口必须放在一个对象中。
解决的办法
通过委托、或者多重继承。
2012-8-3 更新