所在位置:北大青鸟广州天河培训中心 >> 漫谈软件技术流 >>重构以得到模式【转载】

重构以得到模式【转载】

软件模式的伟大之处就在于:它们传递了许多有用的设计思想。那么,如果学了一大堆模式,你就能成为一个相当优秀的软件设计师,对吗?当我学了、用了几十个模式之后,我也这样想过。这些模式帮助我开发了更灵活的框架,建造了更坚固、更具扩展性的软件系统。但是,两年以后,我发现:对模式的了解和使用常常让我陷入过分设计的境地。

设计技能再提升一个档次之后,我发现自己开始以另一种方式使用模式:重构以得到模式,而不是在前期设计中使用模式,也不过早地把它们引入代码之中。这种新的方式来自于极限编程(XP)的设计实践,它使我的设计不多不少,恰到好处。

什么是过分设计?

当你把代码搞得比需要的还要复杂、还要灵活时,你就是在过分设计。有些人这样做,是因为他们相信自己知道系统未来的需求。他们认为:最好是今天就把设计做得灵活些、复杂些,这样它就能适应明天的需要。听起来很美,如果你能未卜先知的话。

但是,如果你的预言错了,你就是在浪费宝贵的时间和金钱。花上几天乃至几周的时间去给设计增加不必要的灵活性,这种情况很常见——结果,给系统加新功能、排除错误的时间就少了。

如果你按照预先的设想写出了代码,但是预想的需要却没有出现,怎么办?你不会删掉它,因为删掉它会比较麻烦,或者你还希望以后能用上这些东西。不管出于什么原因吧,反正这些过分灵活、过分复杂的代码越积越多,你和团队中其他的程序员就不得不在越来越多、越来越复杂的代码基础上工作。

为了补偿这一切,人们决定把系统分成独立的小块,各自负责一块。这看起来能让他们的工作轻松些,但是却有副作用:每个人都按照自己的想法去写代码,很少看看别人是不是已经做过了自己需要的工作,所以就产生很多的重复代码。

过分设计会影响生产力,因为当别人接过一个这样的设计时,他不得不花些时间来了解设计的细微之处,然后才能舒服地扩展或维护它。

过分设计总是悄悄地发生,许多设计师和程序员甚至都没有意识到自己正在过分设计。当他们的公司发现团队生产力下降时,很少有人知道过分设计在其中扮演的角色。

程序员们会过分设计,也许主要的原因是他们不想被糟糕的设计困住。糟糕的设计常常跟代码结合得过于紧密,所以很难改进。我也曾经遇到过这种事,所以我才会这么喜欢使用模式预先做好设计。

模式,万灵药

当我第一次开始学习模式的时候,它们向我展示了一种灵活、精妙而优雅的面向对象设计方法,这正是我非常想掌握的。在系统地学习了模式之后,我就开始使用它们来改进以前的系统、构造以后的系统。由于我知道模式能带来怎样的效果,所以我觉得自己走的肯定是一条正确的路。

但是,随着时间的推移,模式的威力让我对更简单的编写代码的方式视而不见。只要发现有两种或者三种不同的计算方法,我马上就跑去实现Strategy模式,但是实际上一个简单的条件表达式会更简单、更容易实现,也更加实用。

有一件事情让我对模式的专注显露无遗。当时,我和另一个人一起写程序,我们俩已经写了这样一个类:它实现了Java的TreeModel接口,用来在一个树型窗口控件中显示特定对象的图案。我们的代码能够工作,但是树型控件在显示时会调用每个被显示对象的toString()方法,但这个方法并不能返回我们想要的信息。我们也不能修改toString()方法,因为系统的其他部分对这个方法还有依赖。于是,我们开始想办法。习惯所致,我马上就想到了模式。首先浮现在我脑海中的是Decorator模式,于是我就提议用Decorator模式来封装要显示的对象,然后重载封装对象的toString()方法。可惜,我的搭档的回答让我大吃一惊:“杀鸡焉用牛刀?”他创建了一个名叫NodeDisplay的类,它的构造子接收一个待显示类的实例。NodeDisplay类很容易写,因为它的全部代码量还不到10行。而我的Decorator模式起码需要50行代码,要通过很多次委托才能调用到需要显示的对象。
象这样的经验让我知道:我不能再这样过多地考虑模式,必须重新回到短小精干的代码。于是,一次抉择摆在了我的面前:为了成为更好的软件设计者,我费了很大的力气来学习模式;但是现在,为了再提升一个层次,我不能再依赖它们。

欲速则不达

要提高一个档次,还必须学会避免设计不足。相比过分设计,设计不足的情况要常见得多。如果我们单单想着给系统增加越来越多的功能,而不随时留心改善设计,那就很可能会犯设计不足的错误。许多程序员都是这样干的——起码我曾经这样干过。你只管让代码能运行,然后就去完成下一个任务,再也不会回来改进现在写的代码。当然,你希望常常回来维护自己的代码,但是要么因为你自己太忙,要么因为经理和客户催得太紧,最后你还是没有时间。

这是对软件的不尊重,它会使你的软件开发节奏变成“快、慢、更慢”,就象这样:

1. 你很快地发布了系统的1.0版本,但是代码质量很龌龊。
2. 你想发布2.0版本,但是龌龊的代码减慢了你的开发速度。
3. 当你想发布未来的版本时,由于龌龊代码越来越多,你的速度也越来越慢,直到客户和老板对这个系统失去信心。

在我们这个行业里,这种事情是司空见惯的,它会大大降低企业的竞争力。还好,还有另一条比较好的路。

苏格拉底式开发

测试优先的编程、大刀阔斧的重构,这两个来自极限编程的实践彻底改变了我开发软件的方式。我发现,这两个实践既帮助了我,也帮助了整个公司。我们既不会过分设计,也不会设计不足,节约下大量的时间来满足我们的需要:构造良好的系统,按时交付。

测试优先的编程方法把程序设计变成了苏格拉底式问答:写测试就是向你的系统提一个问题,然后你就应该编写代码来回答这个问题。再重复这个问答过程,直到你得到需要的系统。这样的方法大大提高了编码的有效程度。这种编程节奏让我感觉耳目一新:无须再把系统的每个细枝末节都考虑清楚。测试优先的编程方法让我可以先实现主要的功能,然后再根据需要对其进行改进,使其获得所需的灵活性。

大刀阔斧的重构则是这个渐进式设计过程中的一部分。重构是“保持行为的代码转换”,或者按照Martin Fowler的定义,重构是“在不改变代码外在行为的前提下对其做出修改,以改进代码的内部结构,使其更容易理解或修改”。

当苏格拉底要讲一个道理的时候,他总是会采用问答的形式。当受教者答出一个答案时,苏格拉底会再问更多的问题。通过这些问题,他帮助受教者抛开无关紧要的东西,理清混乱的思路,巩固学到的知识。而重构扮演的正是这样一个角色。当你进行重构的时候,你不断地解剖自己的代码,去除其中的重复,使它变得越来越清晰、越来越简洁。

重构也是需要技巧的。你不能制定出一个改进系统设计的时间表,到了规定的时间再重构;你必须在任何需要的时候重构。重构可以提高你的代码质量,从而使你可以保持健康的开发节奏。Martin Fowler等人撰写的《系统重构:改善现有设计》 [1]一书记录了很多重构方法,其中每一条都指出了一种常见的改进需求和完成改进的步骤。

为什么要通过重构得到模式?

在许多不同的项目中,我观察了自己和同事重构的方式。在使用Fowler的书中记载的许多重构方法的同时,我们还是发现模式也能帮助我们改善设计。在这些时候,我们就要通过重构来得到模式,从而避免制造出过分的灵活性或者不必要的复杂度。

“重构得到模式”的动机和“不依赖模式进行重构”的动机是一样的:减少或去除重复,简化设计,使代码能更好地表达自己的意图。

渐进式设计

时至今日,在对模式——“重构产生的设计结构”——非常熟悉了之后,我终于明白:理解重构得到模式的原因,比理解最终的结果或者实现这个结果的细节更有价值。

如果你想成为一个更好的软件设计师,那么,学习好的软件设计的发展过程会比学习这些设计本身更有价值,因为设计发展的过程才是智慧藏身之所。发展最后得到的设计结构可以帮助你,但是如果不知道它发展的历程,你就很可能会误用它,导致你的项目过分设计。

现在,我们的软件设计文献过分集中于好的解决方案,而不太重视这些解决方案的发展演化过程。我们需要改变这种情况。重构的资料能够帮助我们更好地理解一个好的设计方案,因为它揭示了这些方案的演化过程。
对于大多数的模式,我们都必须这样对待:在重构的场景中观察模式,而不仅仅把模式视为可复用的软件单元。这也许是我收集、编写这一系列文章的最初用意吧。

通过学习渐进式的设计方法,你就能够成为一个更好的软件设计师,并且减少过分设计和设计不足的几率。测试优先的编程方法和大刀阔斧的重构则是渐进式设计的关键。学会通过重构得到模式,你会发现自己的能力更强,完全能够做出优秀的设计。

开班信息
咨询热线:020-85566215
名师讲座

软件世界 梦幻体验

讲座时间:周六、日 14:00
1.软件行业真的那么高深莫测?
2.软件技术真的那么难学?
3.学习软件能做什么工作?
4.软件行业可否托付终身?

 让北大青鸟金牌培训师为你
  逐一打开问号!!

订座热线:020-85566216