回头审视一下传统的“软件工程”过程。在软件工程中,十分强调“流程”和纪律,追求软件过程越是“自动化”越好。希望需求是被稳固下来,事先就做好,可以作为“原料”输入到一连贯相互独立“工序”中,通过控制开发人员在每个工序中的作为,使得产品严格按照预先期望的模子生产出来;希望开发者都安分守己地努力,所有的沟通和交流最好都文档化,以分清责任减少争论,同时以文档为驱动,规规矩矩按部就班地做上一个流程的文档给自己规定好的事情。呜呜,正在焦油坑里挣扎的猛犸象还少么?
3、要面向代码
再强调一遍:源代码就是设计!
即使在软件工程中,这一点也是不可否认的(虽然不被承认)。很多人认为,软件设计是指编码之前的“概要设计”和“详细设计”,代码只是“实现”,只是个体力活儿。但是,代码却是软件设计最真实、准确、有价值的表现。在编码前,再好的构思也只是“预设计”,是没有被验证、没有被认可、没有被赋予现实意义的空想。“预设计”再好也并不是设计,在所谓“概要设计”和“详细设计”阶段通常不乏玲珑的构思,但是由于轻视了代码(而去重视那些只是临时使用一下的设计文档),往往能“化神奇为腐朽”,况且没有在代码中被验证与整个系统的协调性的神奇构思,又有什么值得称道的呢?
所以,我们的设计应该面向代码,而不是轻视它。有什么好的设计构思,最好赶紧在代码上表达出来,并很快地验证这个构思是正确的,是与整个系统相容的,而不是把很多构思都堆积在“设计文档”中,掩盖了矛盾和错误,并在编码的时候把它们放大。
设计面向代码,应该从测试用例出发。比较好的方法是采用“测试驱动开发”(TDD)(源自《测试驱动开发》,Kent Beck著)的方法,直接面对需求,把需求用测试用例准确地表达出来,所有的设计都从测试用例出发,这样我们的设计就能够更贴近真实的需求,直接将需求和代码联系起来,减少了中间所有的不确定的环节。明确的需求都是可以测试的,编写测试用例的过程其实是一个促使开发者对需求真正认识的过程,这也是寻找好的软件设计的出发点,实在不应该忽略或轻视。并且有了准确的测试用例做保障,我们在改进过程中随时都可以验证所做的修改的正确性。
设计面向代码,要求在变化中保持代码“健康”。代码是在开发的过程中不停地被改变的,但是随时要保持代码的“健康”。这种改变不是往旧墙上刷新粉,整个过程也不是“code and fix”,特别是软件开发的后期,最好不要有显眼的“补丁”,而应该针对任何变化,最好把所做的修改或加强融入到现有的设计中,使之和谐不留痕迹。至于什么样的代码才是“健康”的,我想这是一个值得展开讨论的话题。
设计面向代码,切忌盲目复用。软件开发为了提高资源利用率和开发效率,希望能够在新的应用中运用已有的设计成果,最大限度地实现软件复用,这种愿望可以理解,但复用一定是有条件有限度的。复用别人现成的代码,天然受到别人设计的制约,要想复用,首先要充分了解别人的设计,特别考察它的“通用性”,由于不是为我们的应用定制的,所以我们的设计要迁就这种“通用性”,这种迁就是相当有风险的,很可能使我们的设计变得僵硬。不记得哪位高人曾经说过“复用只是神话”,我觉得是很有道理的,采用别人的代码的当时是省心省力,但是后期的维护似乎还有漫长的痛苦等着你。再者,应用是有差异的,哪怕相同的应用,削足适履而不是根据脚的尺寸去做鞋,是不可取的。
4、要重构
不知道有没有特别厉害的软件设计高手,可以将好的设计一步到位地呈现出来?不过我想对于大多数人来说,设计并不是一蹴而就,而是循序渐进的。最初的设计可能与最终的设计相差巨大,这是再正常不过的事情。重要的是软件设计要朝一个好的方向演进,重构的过程就是演进的过程。重构是对现有代码的设计进行优化和改良,《重构:改善既有代码的设计》(候捷等译,中国电力出版社出版)一书指出了存在于代码中的常见的22种“坏味道”,并针对性地提供了一些很中肯实用的重构手法,很值得借鉴。其实重构应该是软件开发者的一种自觉习惯,重构手法也是经过长期实践经验已经潜移默化了的。
最初的设计总是丑陋的,重构是使“丑小鸭”变“白天鹅”的必要手段。哪怕再巧妙的构思,也会有制肘,哪怕再谨慎的防备,也会有疏漏,所以设计的雏形总会有这样那样的不足,或多或少地脱离现实问题。这些不足的地方都是可以改进的,关键是有意识地去寻找出这些不足,并纠正它们。因此,我们可以为了更紧密地贴合需求,放心地让我们最初的设计粗糙一些,我们总可以用重构的方法改造它。
好的设计是不断地重构才显露出来的。设计应当是从需求出发,做出一些“粗糙”的解决问题的方案,然后根据经验对该方案不断进行改进、完善,使其越来越灵活,越来越趋于稳定,得到的,自然就是好的设计。而不是从经验出发,先想象出一些“玲珑”的构思,然后修修补补,使其符合我们的需求。前一种方式是借鉴经验,后一种方式是强求经验;前一种方式是慢慢演化水到渠成,后一种方式是急于求成生拉硬拽;前一种方式灵活,有更进一步优化的空间,后一种方式呆板,越修补越走样。优劣自现。
设计过程中要“两顶帽子”轮流戴。“两顶帽子”是Kent Beck为了形象地描述重构过程中的两项任务而引入一种比方。一顶帽子是改善现有设计,另一定帽子是增加新功能。提醒我们,任何时候应该只做一件事(除非你喜欢把两顶帽子摞起来戴):改善现有设计的时候,就立足于现有设计,用现有的测试用例可以很方便地验证改善的正确性;在增加新功能的时候(容纳一种新的需求),就充分信任现有代码的正确性,而立足于该功能,关注在新增功能的测试用例上以保证测试用例的正确,同时修改或增加代码使得所有测试用例(包括新增的)通过。如果觉得新增了功能设计被污染了,别急,测试用例通过后可以换戴另一顶帽子。
5、要新陈代谢
软件设计不是一劳永逸的,技术更新很快,软件设计要为应用服务,就要适应新的技术条件,新陈代谢。软件设计本身要有开放、灵活的架构,而开发者也要有与时俱进、开拓创新的精神。
软件设计要吸纳需求的变化,不断适应新的环境。软件是服务于应用的,应用的背景发生了变化,软件必然要跟着变化,所以好的软件设计要有在变化中存活和发展的生命力,这就要求软件本身的架构是开放、灵活的。软件架构是在软件在开发的过程中,不断纳入新的需求,软件设计自身不断地进行调整,到最后稳定下来的部分。所以软件架构是在需求不断变化的打磨下进化而来的,是软件设计的骨架。只有来自于实际应用的动态的软件架构,才方便改变自己以适应应用环境的改变。
开发者要不断学习,提升自己的眼界。软件应用的领域范围广泛,软件技术的发展也朝不同的方向在不断地提升;软件开发的本质规律还没有完全大白于世,软件业界存在着众多的思想流派,不同流派正对一些技术方法会有一些争议。而开发者往往只能集中精力于某一特定的方向,而对其他方向的发展比较生疏;或者开发者只信奉一个思想流派,对其他不同的意见不屑一顾——这些都是眼界受局限的表现。开发者应当了解其他领域的一些基本情况,其他流派的一些基本主张,这有益于反思自己开发中的一些困惑,启发自己的思路。
开发者还要保持开放的心态,要革故鼎新。好的软件设计,要经受住时代变迁的考验和用户的挑剔。所以设计者要有开放的心态,虚心接受用户的批评和建议,并积极改进软件的设计以满足用户的要求。特别是对于新的有用的技术元素(如新的软件架构理念、新的网络技术、新的数据库技术、新兴的编程语言等),要有一定的敏感度,要从中吸取积极的启发,来加强软件设计的技术跟进。对于一些不合时宜的设计,要更新换代,始终保持软件设计与时代同步。
开发者还要有一点完美主义情节。软件设计是软件开发者心血的结晶,是开发者思想的表白,所以开发者往往对自己成功的作品爱不释手。就像母亲总觉得自己的孩子最棒一样,开发者会觉得自己的设计是最好的。应该对那些倾心尽力做出软件设计的开发者致以崇高的敬意!但是也有必要提醒他们,要理性客观地对待自己作品的不足,还有不完美的地方。这些话,特别需要善意地对那些对现在的“设计”自我感觉良好的人说。相当多的开发者,长期在设计的荒原,已经习惯了在萧瑟的秋风和飞砂走石中缉拿隐藏在荆棘中的bug,还从来没有想象过软件设计其实可以做得更好,没奢望过还有鸟语花香的春天。该醒醒了,不要安于在黑暗的屋子里窒息的命运!
延伸阅读
文章来源于领测软件测试网 https://www.ltesting.net/