[转]包设计原则
2 包的设计原则
要对包进行发布,首先要先设计好包,对规模较大的应用来说,划分包的组合很多,仅仅把看起来像是适合在一起的类放进相同的包中,得到的往往是一种不好的包结构:发布很困难、不容易重用、难于更改等等,这种包结构带来的可能是更多的麻烦。显然我们需要一些原则来指导包的划分,以下列出这些原则,前三个原则用来指导把类划分到包中,关注包的内聚性,后三个原则用来处理包之间的相互关系,关注包的耦合性。
2.1 REP 重用发布等价原则The Release Reuse Equivalency Principle
重用的粒度就是发布的粒度。
如果一个包中的软件是用来重用的,那么它就不能再包含不是为了重用目的而设计的软件。换句话说,一个包中的软件要么都是可重用的,要么都不是可重用的。简单地声明一个类是可重用的做法是不现实的,我们所重用的任何东西都必须同时被发布和跟踪。如果一个包同时包含了可重用的类和不可重用的类,那么当不可重用的类发生变化时,就要进行一次包的发布,而原本不受影响的重用者就需要决定是否采用新版本,以及采用新版本后可能的编译,连接和测试工作。这些内耗操作是应该避免的。
2.2 CRP 共同重用原则The Common Reuse Principle
一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类。
在大多数情况下,一个可重用抽象需要多个类来表达,该原则规定这些类应该在一个包中,而属于不同抽象的类不应该在一个包中。乍看起来,这条原则和 REP 有点相似,但实际上还是不同的,比如,抽象 A 包括类 A1、A2、A3 ,抽象 B 包含类 B1、B2、B3 ,从 REP 原则来看,这个包没有什么不对,因为该包的软件都是可重用的,但它却违反了 CRP 原则,因为重用者完全可以只重用 A 抽象的类或只重用 B 抽象的类,这样当任一抽象的改变都会导致该包重新发布,虽然该发布对某一抽象的使用者是无意义的,但是他们仍然需要需要重新验证和重新发布,这会白费相当数量的努力。
2.3 CCP 共同封闭原则The Common Closure Principle
包中的所用类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对其他的包不造成任何影响。
REP 和 CRP 关注的都是重用性, CCP 关注的是可维护性。对大多数的应用来说,可维护性的重要性是超过可重用性的。一个变化(包括需求上的,设计上的等等)可能会引起多个类的更改, CCP 要求我们把这些类放在一个包中,同时把那些不受影响的类放到其他的包中,因此一个包只有一个引起变化的原因,一个变化只对一个包产生影响,这样大大减少了重新发布的次数和重新发布对其他包的影响,从而提高了软件的可维护性。
2.4 ADP 无环依赖原则The Acyclic Dependencies Principle
在包的依赖关系图中不允许存在环。
如果包的依赖关系图中存在环,就会导致环上的所有软件包必须同时发布,如下图,
由于P3使用了P1中的一个类而形成了依赖环,若要发布P1,必须先发布P2和P3,而发布P3又要先发布P1和P2,P1、P2、P3实际上已经变成了同一个大包,于是工作于这些包上的开发人员不可避免地会遭受晨后综合症。
去掉依赖环的方法有两个:使用DIP。如图,把Y的接口和实现分离,
增加新包。如图,把 P1 和 P3 都依赖的类移到一个新包中
2.5 SDP 稳定依赖原则The Stable Dependencies Principle
朝着稳定的方向进行依赖。
如果一个包被很多包所依赖,那么它就是稳定的,因为要使所有依赖于它的包能够相容于对它所做的更改,往往需要非常大的工作量。一个系统中的所有包并非都应该都是稳定的,因为如果那样的话,系统将很难更改。 SDP 指导我们处理稳定包和不稳定包之间的关系:不稳定的包应该依赖于稳定的包,一个包应该依赖于比他更稳定的包。你设计了一个不稳定的包,期望它能随变化容易地更改,可当它被一个稳定的包依赖后,它就再也不会易于更改了,这就使软件难于修改和变化。
2.6 SAP 稳定抽象原则The Stable Abstractions Principle
包的稳定程度应该和其稳定程度一致。
一个系统的高层构架和设计决策应该被放进稳定的包中,因为这些构架决策不应该经常改变,然而稳定包的不易更改的特点会使这些架构决策不灵活,显然只用稳定性来度量一个包是不够的, SAP 告诉我们:稳定的包也应该是抽象的。它应该包含抽象类,系统通过在其他包中实现该稳定包中的抽象类来进行扩展,而该稳定包无需修改,从而在保持稳定的同时也不失灵活性。
3 包的设计过程
几年前当我还不知道这些包的设计原则时,认为包就是系统的高层的功能分解,应该在项目开始时就设计好,结果遭受了失败。实际上,包的依赖关系图和描绘系统的功能之间几乎没有关系,它是系统可构建性的映射图。项目开始时,我们还不知道系统中有哪些类,更不必说它们之间的关系,在这种情况下不仅很难创建出包依赖关系图,即使创建出来也很可能是不合理的。包的依赖关系结构应该是和系统的逻辑设计一起增长和演化的,项目开始时不必考虑包,系统仍以类的粒度组织,随着开发的进行,类越来越多,对依赖关系进行管理,避免项目开发中出现晨后综合症的需要也不断增长,此时我们应用 CCP 原则对把可能一同变化的类组织成包进行发布,随着系统的不断增长,我们开始关注创建可重用的元素,于是开始使用 CRP 和 REP 来指导包的组合。最后使用 ADP、SDP、SAP 对包图进行度量,去掉不好的依赖。
4 你真的采用了包的发布机制了吗
如果说某个做产品的团队没有采用包的发布机制,它也许会大叫冤枉:我们明明是进行了发布的呀,你看,这不是我们发布的1.0、1.2版本吗?事实上,任何一个产品都离不开包的发布,只不过对这样的团队而言,他们系统里真正意义上的包只有一个,那就是整个系统,而该包的发布常常是在经过了晨后综合症的洗礼之后,是在开发基本完毕后进行的,实际上,我们更应该在产品的开发过程中使用这一机制,这样,产品的开发过程才会以合理,有序的方式进行,产品才能尽快地交付。