- 包可以用作包容一组类的容器。通过吧类组织成包,我们可以在更高层次的抽象上理解设计。
- 我们也可以通过报来管理软件的开放和发布。
- 类会和其他包的类存在依赖,跨越包的边界。包之间会产生依赖关系。
一、如何划分包??
1粒度:包的内聚性原则
1.1重用发布等价原则
包的重用粒度就是包的发布粒度
当重用一个类库的时候,你可能有如下需求:
- 清晰的接口和文档
- 维护这些代码
- 接口和功能变更时通知一下你,并且尽量保证系统的兼容
- 旧版本提供一段时间的支持,你有权在一段时间
因此我们得出结论
- 重用性时基于包的,可重用的包包含可重用的类。包应该包括一组可重用的类。
- 如果一个包中的软件是用来可重用的,那么他里面的内容应该全是可重用目的写的,不能包含不可重用的类。
- 一个包的内容应该是为了同一个重用的目的编写的,比如:一个金融框架的功能不能喝一个容器类库一起发布。
1.2共同重用原则
一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么重用包中的所有类。
包经常以JAR、DLL的形式出现。如果被使用的包以JAR包的形式发布,那么使用这个包的代码即使只依赖这个包的一个类,也将依赖这个包。所以如果JAR包的内容进行了修改,即使是非依赖的类进行了修改,也会影响使用者重新验证和发布新的版本。因此我们希望,一个包中的类都是不可分开的,仅仅只依赖其中的一部分情况尽可能使不可以出现的。
1.3共同封闭原则
包中的所有类对于同一类性质的变化应该是共同封闭。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他的包不造成影响。
我们要尽可能将同样原因的所有类共同聚集在一个位置,我们不希望为了一个单一功能的修改,分布在多个包中。
二、如何管理包的相互关系??
2稳定性:包的耦合性原则
2.1无环依赖原则
在包的关系图中不允许存在环
是否有这样的经历?工作了以整天,完成了某项工作回家了,但是第二天过来发现软件功能不能工作了,原因是有人更改了你依赖的包。他比你走的更晚。在缺乏纪律的团队中,几周无法构建出一个稳定的项目版本是一个很常见情况。
我们有两种构建系统的方式,
- 方式1,各个团队定期约定时间进行集成,然后统一验证,这种方式的集成对大型系统来说,集成复杂度是非常高的,往往几天无法构建出一个系统。一个团队的问题往往引起所有团队阻塞。
- 方式2,各个团队维护自己的包,其他开放团队决定是否马上采用这个心版本。因此所有开放团队都不会受其他团队的支配。这是一个非常简单和合理的过程。
上图展示了一个应用程序典型的包结构。pkg6维护的团队发布一个新版本时会容易找出受到影响的包,如pkg4和pkg1。完全不会影响到系统中许多其他的包。他们不知道也不关心何时对pkg6进行了变更。这很好,因为pkg6的发布对系统影响很小。
当工作于pkg6的团队想要运行该包的测试时,只需要把自己版本和系统中其他包编译链接即可。他不需要依赖其他的包。pkg6的开发人员是比较幸福的,他不依赖别人。
对于发布整个系统时,也只需要自底向上的进行。首先编译、测试pkg7的包,接着是pkg5,然后是pkg6,依次向上。这个过程非常清楚。
循环依赖将打破上面所有假设。假如存在循环依赖将很难定包的构建顺序,这种环境很难对包进行隔离,在C++项目中编译时间也会几何级上升。解除循环依赖的关系在我的一篇文章中有介绍,这里就不重复。
3自顶向下设计是理想的
包的设计不能自顶向下的。这意味着包的结构不是设计系统中首先考虑的事情。包的结构会随着系统的增长而变化演进。
也许你会认为这是违反直觉的。我们已经认为像包这样的大粒度的分级同样也是高层功能分解。当我们看到一个向包依赖关系这样的大粒度分组时,就会觉得应该以某种方式描绘系统功能。然而,这可能不是包依赖关系图的一个属性。
事实上包的依赖关系图和描绘应用程序的功能之间没有任何关系。它们是应用程序可构建性的映射图。这就是不在项目开始设计它们的原因。包的依赖关系结构是和系统逻辑设计一起增长演化的。
4稳定依赖原则
朝着稳定方向依赖
还是如图1所示,pkg7是被依赖最多的,他是非常稳定的,pkg1是没有人依赖的,他是非常不稳定的。
并非所有包都应该是稳定的,如果一个系统中所有的包都是稳定的,那么这个系统将是无法修改的。一些包是稳定的一些包是不稳定的。
如图所示,第一个依赖我们认为是合理的。而第二个依赖是不合理的,不稳定的包应该尽量不要放到下层被很多包依赖。系统中的某些软件不应该经常改变,比如socket通讯,有比如文件系统。
5稳定抽象原则
包的抽象应该和包的稳定程度一致
一个稳定的包应该是抽象的,这样它的稳定性就不会使其无法扩展。另外,一个不稳定的包应该是具体的,因为它的不稳定性是的其内部的具体代码易于修改。
怎么理解呢,比如我们经常把接口抽象放到一个包里,这个包是稳定的,但是系统还是保留的扩展变化的能力,因为实现是可变的。又比如我们的具体类比抽象接口的实现更好修改。