Java应用架构读书笔记(5):处理复杂性

 由  罗俊杰 发布

复杂性与技术债务

随着系统的演化,系统的复杂性会越来越大。每一次发布,系统的大小就会增加,从而复杂性也会增加。

Spring框架在2003年最早的发布版本中有一万多行代码,到了2.5版本时,代码数量大概翻了六倍。

系统维护和演化的开销大概站了总开销的百分之90以上。

技术债务是Ward Cunningham提出的一个隐喻,用来描述为了赶进度或者满足用户期望所做的设计上的折中。

债务需要及时的处理,否则系统的设计将会持续的腐化,太多的技术债务的积累最终会使得系统崩溃。

设计的腐化

腐化软件最常见的原因是具有过多依赖的紧耦合的代码。

过多的依赖会导致:

  • 妨碍维护性:一处修改导致多处修改,修改有时不可避免,但是好的依赖结构可以让修改更加容易,因为开发者能够完全把握修改的影响。

  • 阻止扩展性:灵活的架构需要对扩展开放对修改封闭。过多的依赖通常是由于不恰当的使用抽象,以及难以扩展的地方没有使用抽象。抽象可以帮助开发者暴露定义良好的扩展点,同时封装实现的复杂性。

  • 抑制可复用性:我们通常强调类级别的复用,为了达到更高层次的复用,我们需要仔细考虑包和模块的结构。复杂的包和模块的依赖会极大地减少复用的可能性。

  • 限制可测试性:类之间的紧耦合会使得类无法进行独立的测试。同样,过多的依赖会使得模块无法独立的进行测试。不进行广泛测试的团队无法快速响应变化,因为测试的缺乏会导致无法轻松获取变化所带来的影响。

  • 妨碍集成:当独立的模块集成到一起时,性能的退化,行为粒度不合适,事务不兼容等问题会出现。

  • 影响可理解性:具有复杂依赖的接口是难以理解的。

循环依赖

循环依赖可能出现在系统的各个层次中,一个层次中的循环依赖可能导致另一个层次中的循环依赖。

循环依赖很容易不经意间加入到设计中来。

事实上,如果你不考虑物理设计而仅仅考虑逻辑设计,不管逻辑设计多么漂亮,最终你可能并不能达到你想要的效果。 比如你需要考虑类在各个模块中的分布,一个没有循环依赖的类设计,如果类在模块中的分布设计不合理,也可能导致有循环依赖。

可以通过测试驱动开发,或者一些静态分析工具来避免循环依赖。

类之间的循环依赖和包之间的循环依赖有时候是可以容忍的,只要不导致模块的循环依赖。模块之间绝对不允许有循环依赖。

连接点、模块和SOLID原则

SOLID原则并非任何地方都需要使用,因为更大的灵活性必然导致更大的复杂性。 系统都有连接点,即模块连接的地方。系统中的连接点需要最大程度的灵活性和弹性。因为连接处的改变对系统的影响比模块内部的改变要大得多。 这些连接点,或者叫做模块边界,就是运用SOLID原则的地方。

如果将SOLID原则和模块模式有机的结合使用,那么我们将获得如何最好的设计大型软件系统的有效指导。

对于维护系统复杂性来说,类过于细粒度,一个中等大小的系统可能包含上千个类。模块提供了一种更加粗粒度的单元来帮助我们封装复杂的设计决策。

模块化的好处

复用是被提得最多的一个优点。

降低复杂性,增加灵活性,从而减少维护开销是另外一个重要的好处。

此外,在运行时也有一些好处,比如多版本部署,热部署等。

查看评论