设计,看上去很美 wayfarer

时间:2022-09-14 17:01:07

设计,看上去很美

    设计没有标准,模式充满变化,我们对设计与模式的探讨,就是希望能从没有标准的设计中体验设计的乐趣,从充满变化的模式中寻求问题的解决之道。
    我这里所谓“设计没有标准”,其实并非没有标准,现实是设计的标准实在太多了。我们都希望找到最好的设计方案,然而什么是最好,每个人都有自己的“哈姆雷特”。满足客户需求的设计就是最好的,这个结论我想不会有人反对,前提是,怎样通过设计来满足客户需求?

    计划的设计和演进的设计

    通常来说,软件设计不外乎两种方式:计划的设计和演进的设计。很多人看来,计划的设计更符合工程学的理念。如果你要建一间茅屋,那么你只需夯好土墙,再胡乱堆放一些茅草置于屋顶之上就可以了。然而,如果要你建一座苏州的拙政园,就必须事先有计划的设计了。哪里应该堆放假山,哪里应该开辟池塘,亭子的形状,院落的分布,乃至于园内的一花一木,无不需要独具匠心。软件设计也是如此,且过之而无不及。接手项目的时候,首先考虑的不是编码,而是考虑整个系统的架构,根据需求考虑系统中的重大问题。模块的功能,模块间的关系和系统分布的层次,都需要匠心独运,从一个抽象的层面来考虑。
    演进的设计恰好与之相反,它是一种渐进的过程。它并不要求前期的设计有多么的完美,实现的需求有多么的完整,你只需要把现阶段考虑的问题编码实现就可以了,随着演进的深入,编码也会随之而修正,最后设计会逐渐丰满起来,经过一系列的方法,最后的设计也渐趋完美。
    你也许会认为“演进的设计”如此的简陋与平庸。没有计划,只会令设计一团遭。但我需要提醒你的是,虽然都是工程学,软件的设计并没有建筑设计那么简单。因为,你很难在设计之初,考虑到客户的全部需求,甚至于实现未来的扩展。在设计一开始,你能确信:
    你对客户的需求都理解了吗?
    你能确定客户的需求不再变化吗?
    你设计的软件架构真的能满足需求吗?

    是的,你无法给出肯定的回答。总之我在这里不是想说服每个人,要采取哪一种设计方式。事实上,我也面临抉择的困难。

    过度设计,还是简单设计?

    Kent在《解析极限编程——拥抱变化》中为简单系统制定了四个评价标准,依次为(最重要的排在最前面):
    通过所有测试;
    体现所有意图;
    避免重复;
    类或者方法数量最少;

    这些标准写出来简单,要根据这个标准来实现,就不是那么容易的事了。我相信,软件设计人员都希望自己的设计尽可能简单。然而,在设计时,我们不仅仅要考虑软件的功能,我们还要考虑软件的性能、扩展性,模块间的耦合关系,系统的稳定、部署和更新,版本的管理,系统的安全,界面的友好程度。要想简单,何其之难!
    Do the simplest thing that could possibly work! 这是XP人士大声疾呼的口号,我也举双手赞成。问题是,我们需要让简单的事情,同时又有效。很多人在设计时,并不满足于实现眼前的功能。看到加法,他们可能还会想到乘法;虽然目前的需求是整数,他们可能想到今后可能会扩展到实数,甚至于复数。他们希望能利用某种设计,使其具有更好的扩展性。从眼前的需求来看,可能是过度设计;然后对于未来,这个设计才是最完美的方案。
    问题不在于设计是否过度,关键还是在于设计的理念。是只做目前需要的事,还是未雨绸缪,想好今后的功能扩展?这个问题的答案还需要实际的项目开发来检验,根据不同的需求,答案会因此而异。

    需要设计模式吗?

    答案是肯定的,但你需要确定的是模式的应用是否过度?我得承认,世界上有很多天才的程序员,他可以在一段代码中包含6种设计模式,也可以不用模式而把设计做得很好。但我们的目标是追求有效的设计,而设计模式可以为这个目标提供某种参考模型、设计方法。我们不需要奉GOF的设计模式为圭臬,但合理的运用设计模式,才是正确的抉择。
    很多人看过GOF的《Design Patterns》,对这23种模式也背得滚瓜烂熟。但重要的不是你熟记了多少个模式的名称,关键还在于付诸实践的运用。为了有效地设计,而去熟悉某种模式所花费的代价是值得的,因为很快你会在设计中发现这种模式真的很好,很多时候它令得你的设计更加简单了。
    其实在软件设计人员中,唾弃设计模式的可能很少,盲目夸大设计模式功用的反而更多。言必谈“模式”,并不能使你成为优秀的架构师。真正出色的设计师,懂得判断运用模式的时机。
    还有一个问题是,很多才踏入软件设计领域的人员,往往对设计模式很困惑。对于他们来说,由于没有项目的实际经验,OO的思想也还未曾建立,设计模式未免过于高深了。其实,即使是非常有经验的程序员,也不敢夸口对各种模式都能合理应用。

    重构是必然的!

    既然我们无法给出一个完美的设计方案,因为客户的需求总是变化的,重构也就成为必然。问题是,这样没有添加任何功能的重构,你是否愿意为此付出精力、时间去完成。当客户要求的Deadline将要到来的时候,你还认为你的重构工作是必要的吗?
    有时候,软件设计常常身不由己。然而,纯从技术的角度来看,重构非但必然,而且重要。既然我们都明白,复杂的未尝就是好的,简单的也不一定是容易的。要保持你的设计尽可能的简单,可能你还需要时时借助重构的利器,来“改善你既有代码的设计”。
    对于重构,Martin Fowler给出了很多条款。这些条款并不是政治课本的教条,也不是“日月神教”的神奇咒语,念着它们就可以防身。这些条款确实很重要,但你需要的是学会他后,然后忘记他,就象张无忌学太极拳那样。我不是故弄玄虚,事实上只有这样,重构的精神才能完全融入到你的设计中。

    UML重要吗?

    我现在看一个设计方案的时候,更希望先看看UML图,然后再看文档的实际描述。如果让我读一段代码,我希望能先看看类图,或许更容易理解代码的含义。UML在OO世界里像是世界语,它便于程序员间的交流,让别人更容易理解你的意图。同时,在设计UML图的过程中,也是一种对思路的清理,对客户需求的把握,设计思想的跟踪。
    UML是一种基于对象的统一建模语言,它能够为系统设计提供清晰直观的设计。在面向对象世界里,UML的地位弥足轻重,甚至被称为是软件设计的一场革命。对于有计划的设计,UML的价值就体现得淋漓尽致。如果我们要清晰地表现模块的功能,模块间的关系和系统分布的层次,使用UML可以使设计师减少很多麻烦,同时降低了语义描述的二义性。然而,如果我们在做演进的设计时,UML还有那么重要吗?我们只需要对眼前的需求进行编码、测试,然后重构。可能我们只需要在Pair Team中讨论设计方案,在预定技术框架内探讨实现的可能和细节。我们完全可以抛开UML繁琐而死板的设计,毕竟最能忠实体现设计思想的,不是文档,不是用例图或是什么类图,而是代码。
    那么,有多少人是这样想的?

    TDD、单元测试和其他

    软件的生命是什么,是质量!而保证质量的唯一方法,就是测试。传统的软件开发过程,强调首先进行需求分析,再从需求分析中抽象出概要设计,进而作出详细设计,然后编码,最后才是测试以验证代码的正确性。而测试驱动开发(TDD)改变了编码的过程,开发仅仅包括三方面的活动:编写测试用例,编码并进行测试,重构代码以消除重复代码使其更简单、更灵活、更容易理解。通过测试来驱动开发,听起来是那么的离经叛道,然而实施起来,又是那么合理、正确和简单,前提是:我们不能在一开始就获得正确的设计!TDD避免了对不完整需求造成的不成熟的设计。通过单元测试,保证了代码的正确性与高质量;通过重构,使设计更加简单、灵活。