2014年2月25日星期二

过渡设计

过渡设计其实是一个很有趣的主题,因为设计是一种人为的主观行为,并不太容易受到制约。而且设计也很难界定,大体上当你打开你的开发工具,并在键盘上敲下第一个字母的时候,设计的内容已经开始在你的头脑中展开了。但其实在软件领域里面,提到的设计大约都是指的架构设计,架构设计是没有什么所谓过渡设计这么一说的,因为架构设计和业务场景关联的十分紧密(适用一切的架构是不存在的),否则就是失败的,而且这一点也十分的容易发现,并不需要软件方面的专家。

而我们所说的过渡设计其实存在于实现的设计中。是的,实现的设计,这个设计在软件工程中似乎并没有太大的地位,但也并非不被重视。主要是这个阶段的设计比较难做,同时也很难管理和控制。个人觉得,在多数的公司中,都没有明确的提出实现设计,并将其列为软件质量的一个重要指标。

但其实实现设计中的过度设计也并不难发现,首先要明确一点,不能因为可能过度设计就不去设计了。并从而否定面向对象思想或者面向方面的开发方式。毕竟我们总是需要一种可以思考的方式并且需要能够和别人沟通和交流。因为实现的过程不可能是孤立的,任何软件都几乎不可能一个人完成。除非你是特殊材料制成的,我们一般管这种人叫天才。不过这种人太少见了,不必考虑他们对于实现的影响。

首先,设计是为了应对变化。也就是说,我们通过各种手段去进行所谓的设计,其目的就是为了让我们的代码有潜在的被改动或者被扩展的这种可能性。比如我们引入了某个接口,这样我们就有了替换实现的可能性。接口在设计中其实就是为了隔离实现的。另外我也可以使用多态性通过改变子类的行为的方式来扩展我们的业务。没有人可以保证业务是永远不变化的,其实业务或者业务的实现方式或多或少都会随着时间而改变,而且时间越长,这种变化就可能会越剧烈。因此支持变化往往是很有必要的。

其二,设计总是为了更好的和业务契合,也就是固化某些核心的逻辑。换一种说法就是在创造某一种DSL语言,这让我想起了格林斯潘第十定律。这也是非常重要的,核心业务往往就是某种产品或者某家公司赖以生存的根本。因此通过一些手段固化这些业务是十分重要的。而且核心业务往往很久都不会发生变化。比如QQ的聊天功能,office的编辑功能,游戏的价值观等。所以设计总是需要为业务来提供便利,越复杂的业务,这个需要就越发的明显。

第三,设计是为了更容易的扩大开发的规模。通过适当的抽象和合理的封装以及分层,把系统的核心和外延分离,以便于能够通过增加人手来提高开发的效率,同时也要避免初级程序员的破坏性。这也是为什么很多的系统喜欢使用spring来开发的原因。使用spring的系统从这个意义上来说,应该不是非常重要的系统。

最后,设计往往应该能够更好的支持测试。开发者往往都不怎么喜欢测试,一般来说他可能更相信自己的直觉。当然,直觉有的时候准确的可怕,但有的时候却错的离谱。所以用多种手段来保证开发的正确性是十分必要的。而测试往往具有很多的层次,对于实现的设计来说,测试更多的指的是单元测试。为了能过更好的支持单元测试,往往需要在设计上进行一些让步。越核心的功能,单元测试就越重要。特别是类库的设计,必须要考虑各种各样的情况,如果没有单元测试,根本无法保证类库的健壮性。

除此之外的设计大多数都是过渡的设计,比如我们为何使用依赖注入技术?因为我们有很多的依赖关系需要管理,而且在不同的层次上希望能够降低耦合性。IOC并不会带来代码阅读或者维护上的便利,因此如果你的系统是一个层次分明,功能确定的系统,IOC其实就是一种过渡设计。所以我发现大多数应用了spring框架的系统往往都是过度设计了的。因为其实你的系统完全不需要那种千篇一律的层次划分和解耦方式。

过渡设计还有一个有趣的特征,那就是一旦有了过渡的设计,为了让这个设计看起来合情合理,那么往后的设计就会有越来越过渡的趋势,直到被废弃。这时你会发现所谓框架的代码量比你业务的代码量要大得多。我把这种情况称为过度设计漩涡,一旦卷入,就无法脱身,并且随着代码的增加,设计的填补,设计会越来越过渡。

使用springhibernate这样的框架来解决问题,似乎已经成了java开发的一种趋势。但其实这些框架掌握起来并不容易,而且一旦使用,往往都会陷入过渡设计的漩涡。严重影响系统的可读性和运行性能。而且你一旦学会了spring或者hibernate的思考方式就更为的可怕,因为他们总是把问题搞的很复杂。复杂的框架以及其运作的方式会把你的业务搞得杂乱无比,你甚至可能还需要去学习一种框架专用的DSL。我觉得这种行为其实就是舍本逐末。

没有评论: