07
2012
04

C# 可复用的代码

刚开始干这一行的时候,对代码的复用有很高的热情。那时候总是希望自己写出的function、class、模块都是可以复用的,能够优美的解决所有问题。但是往往事于愿违,设计的变更、需求的变更、种种没有预料的情况最终把自己的代码摧毁的面目全非。有时一个简单的function会出现各种不同的版本,SendMessage、SendMessage2、SendMessageEx……在注释中说明其间微妙的区别。复用的计划最终破产。经历打击后,又走向另一个极端:使用copy-paste解决问题。不在乎代码的复用性,放弃优美,走向彻底的实现主义。各种bad smell不断出现在代码中,疯狂的复制粘贴,然后稍加修改。一旦变更,到处修改。面对需求变更、产品升级、杂乱纠缠的代码、过时的文档,真是一种痛苦的经历。

代码复用到底能不能做到呢,怎样才能做到复用呢?

代码复用要求设计具有较高的质量

设计和编写可以复用的代码不是一件容易的事情。简单的说,就是要在需求,概念和最终的实现之间建立一种简单的关系[The Aspects Blog]。一个概念或者需求最好能在实现中有一个直接的体现。比如说,设计一个公司的管理系统,公司里有雇员,那么就设计一个Employee类,表示雇员这个概念。雇员有工资,那么就为这个Employee类加上一个Salery属性,表示雇员的工资。需求和实现之间的关系最理想的情况下应该是1:1,即一个需求对应一个唯一的实现。这样,当需求变化的时候,需要修改的实现也只有一处。这样的情况下,代码的复用性也是最好的,同一个功能只用一份代码来完成。

“需求:实现=1:1”是一个最优状态,达到这个状态首先要对需求有清楚的了解,尽量排除需求的含糊性,这一点是基础。其次在设计上要遵守一些最基本的原则,避免不合理的依赖关系。达到这个1:1很难,一般都会根据具体情况有所权衡。

在设计的不同阶段应该采用不同的抽象层次,不能过早的将问题具体化。具体化的时间过早,往往就意味着一个需求会对应多个实现。一些设计者往往不注重抽象,急于将功能点落实,采用的设计方式经常是这样:需求调查,根据用户的需求提出软件解决方案,根据这个方案进行数据库设计,设计数据库表的字段和关系。数据库设计完成之后,业务逻辑就整理清楚了,再经过简单的程序设计,就开始编码了。编码中很多是对数据库表的直接操作。业务的描述通常是这样:按下某个按钮,打开某个画面,从某个数据表中查询某种数据显示在界面上,按下某个菜单,修改数据表中某个字段……这就是从UI到DB的直接操作,不存在抽象的层次。这样的设计对于需求变化的适应程度是很有限的。

软件的开发和维护过程中经常发生需求变化,需求的变化往往处于不同的抽象层次。比如有一个设备的监控系统,管理着电动机、变压器、压缩机等多种电气设备。用户提出这样一些需求:

1.每次采集到设备的数据后,发现设备的电压大于额定电压50%的时候,自动切断设备的电源。
2.变压器的电流大于额定值的10%的时候,要在界面上显示红色的灯。

这两个需求处于不同的抽象层次,需求1是针对较为抽象的“设备”,而需求2是针对较为具体的“变压器”。这两个不同的抽象层次都应该在设计和编码中有所体现。

如果后来用户变更了需求:“当设备的电压连续一定时间大于额定电压的50%的时候,才将电源切断,否则不切电源,并且在切断电源2秒后,要重新尝试连接电源,发现电压仍然超过,就断开不再连接。”这样的需求变更就应该在“设备”这个抽象层次上进行。如果没有这个抽象层次,这个变更就比较麻烦了。

代码在项目内部,和项目之间的复用

谈到代码复用,最初的想法是设计一个完美的模块,一劳永逸的解决眼前的和长远的所有问题。其实复用也有限制,一个产品或者项目要解决特定的问题,脱离了这个具体环境,很多问题的解决方式是不同的。一般来说,我们的目的应该是写出在项目内可以复用的代码。代码在项目内部可以复用,这应该是设计者追求的目标。一旦达到这个目标,就意味着产品已经有了一个稳定的结构,进入了一个可以高效开发的阶段。复用的代码可以减少bug的发生,减少需求变更的影响。

随着客户业务的不断变化,需求难免要发生变化,这时候,要适时的对设计进行重构,以适应新的需求。原有的代码可以最大限度的复用,很多业务模块不必重新编写,推出新版本的压力就会减少很多。

代码在项目之间的复用有很多形式,可以把某个function复制粘贴到新的项目中,也可以把某些文件引入到新的项目中。这都不是复用的最佳形式。代码在项目间的复用一般以“包”作为最小单位。创建者发布一个包,使用者根据需要决定是否使用。使用的形式可以是静态的编连或者是动态调用。当包的维护者发现了bug,或者添加了新的功能,会在修改后重新发布这个包。使用者根据自己的需要决定是否升级。始终只有一个“包”在进行维护。这才是复用的最佳形式。

“复用别人的还不如我自己写一个呢。”真的吗?

我们经常有这样的体会:为了复用别人的代码,我们需要学习很多东西。查资料,看文档,读例子,甚至要读代码。有时候这个学习的时间甚至会比自己写一个相同功能的代码更长。于是,很多人在工作中不喜欢复用代码,而是倾向于自己写一个。“复用别人的还不如我自己写一个呢。”果真如此吗?

先考虑这个问题:这个“自己写一个”的时间是单纯指编码的时间,还是完整的开发时间。编码只是开发的一个阶段。自己写一个也许不难,也花不了太多时间。但是要测试、修改bug、完善文档……一系列的工作都做下来,这个时间一般都要长于复用代码的时间。

还有一个问题:自己写一个,很难做到好的复用性。下次遇到相似问题,只好再写一个。还不如现在就研究一下别的模块怎么用。

从管理的角度来说,随着项目人员的变化,这些“自己写”的模块将越来越难以维护。一些成熟的模块已经有了完善的文档,比较容易找到相关的资料,学习使用的过程也比较容易交流。开发的时候要使用能买到的最好的工具,也要复用能得到的最合适的代码。
« 上一篇下一篇 »

相关文章:

html代码和文本的相互转换  (2012-3-29 16:31:28)

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。