代码改变世界

顺变者昌

2011-11-09 16:33  javaspring  阅读(154)  评论(0编辑  收藏  举报

 “夫兵形象水,水之行,避高而趋下;兵之形,避实而击虚;水因地而制流,兵因敌而制胜。故兵无常势,水无常形。能因敌变化而取胜者,谓之神。故五行无常胜,四时无常位,日有短长,月有死生。”                                                                                                                                                                                                                                                      -----摘自《孙子兵法》虚实篇

大意为:带兵的基本战术要像水往低处流那样,不吃眼前亏,避其锋芒,从敌人的薄弱之处打击。能因敌人变化而变化,然后取胜的人,可以称他为神。所以五行(即金木水火土)相克,没有常胜的;四时(即春夏秋冬)都要变化,不能总停在一个季度;太阳光照射有直射(即‘短’)和斜射;月亮有的时候不能被看到。

这说明任何事物都是在不断变化的,只有顺应变化才能长盛不衰。


纵观软件开发思想经过的几十年,笔者斗胆立论:一切因变而生,顺变者昌!

  

微软CEO鲍尔默曾经说过:"在微软,唯一不变的就是变化!"                                      -----摘自李开复《世界因你不同》

 

回顾最早的机器语言时代,软件开发人员在苛刻的硬件条件下“呕心沥血”,随着高性能计算机的发展,无论那内存,外存,CPU等都有了长足发展,程序开发也进入了一个新的层次,不再纠结于用巧妙的算法降低系统压力,于是出现了高级语言。可以说硬件的变化再促进着软件的变化,也可以说软件开发要求硬件变化。

 

早期的用高级语言编写程序,采用面条式的开发,即代码的控制结构复杂混乱而难以理解,程序的流程像一根根面条一样交织在一起,难以分离。结构混乱,难以理解,必然导致测试排错困难,后期维护困难,不能应对发展变化的需要,淘汰出局实属必然。


所以结构化程序设计应运而生,采用面向过程的软件开发方式,把需求看成一个个的业务流程,然后把流程分类组合在一起,变成一个个功能模块。典型的代表就是用一个个函数实现需求。


随着软件要实现的业务逻辑不断复杂化,面向过程的软件开发暴露出很大弊端,它关注业务流程,可是,做过软件需求的人知道,无论多么用心的与客户沟通,多么努力了解需求,也是无法准确把握所有需求的,而且需求是不断变化的,随着时间的变化,随着公司业务的变化等等,例如今天中国移动存100话费赠100话费,明天变成了赠50,后天又变成了赠桶食用油,再过两天又成了存话费赠流量,赠短信等等业务逻辑层出不穷。

客户永远无法向你保证:我要的就是这个东西,永远不会变。


忘记是哪位大师级人物曾说过:"没有不变的需求,世上的软件都改动过3次以上,唯一一个只改动过两次的软件的拥有者已经死了,死在去修改需求的路上。"


需求的变化必然导致业务流程的不断变化,更何况,还要考虑硬件设备,执行效率等等一系列问题,变化无处不在。面向过程的开发是不能应对这种变化的,究其原因主要为:面向过程的软件开发是分功能模块,通过函数调用实现,改变业务流程就必须修改函数,但这个函数到底关联多少个函数很不容易弄清楚,函数的直接开发者花些时间或许还能理清楚,可是了解软件行业的人都知道,人员流动性很大,不可能这个系统是你的开发的,后期维护依然是你,如果换了另一个人接替你,他还能搞清楚这些函数之间的关联吗?结果便是牵一发而动全身,时间都浪费在寻找Bug上。


所以,面向过程的开发不能应对需求变化,无法适应软件的发展。写到这里,读者可能已经心知肚明了,接下来你要讨论的是面向对象的软件开发了。不错,面向对象的软件开发就是为了解决变化带来的问题。


做过面向对象程序开发的人一般都知道,面向对象的几大特点:抽象,封装,继承,多态等。其实面向对象不仅仅局限于此,面向对象的思想已经涉及到软件开发的方方面面:面向对象的分析(OOA),面向对象的设计(OOD)、以及我们通常说的面向对象的编程实现(OOP)


下面笔者从下面几个角度说明面向对象程序开发是如何应对变化的:

1、面向对象的分析与设计

相对面向过程的设计,面向对象的设计显得不是很直截了当(这也是面向对象代码量比面向过程代码量多的原因),为了开发出可维护,可扩展,可复用,灵活性好的软件,相对来说,面向对象的设计是比较困难的,随之出现的一些工具和框架便是辅助我们做出更好地面向对象设计。

下面,笔者仅从“应对变化“的角度阐述一下这些工具和框架模式的特点:

 

1)UML

UML是对软件系统进行可视化建模的一种语言,UML9种图贯穿软件开发周期的每一个阶段,尤其在软件密集系统的架构层次方面表现突出。从应对变化的角度来说,一张图胜过千言万语,随着软件需求变化,我们可以通过UML图准确定位需要变更的地方,可能引起的其他变化,不会出现我们上面说到的浪费时间寻找Bug的情况。

2)RUP

RUP是面向对象开发的方法论,其中描述了如何控制、跟踪、监控、修改以确保成功的迭代开发。RUP通过软件开发过程中的制品,隔离来自其他工作空间的变更,以此为每个开发人员建立安全的工作空间。说白了就是,RUP能更好地应对软件变更。

3)分层

分层同样是为了封装变化,分层最典型的代表是三层架构,即将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。分层目的即为了“高内聚,低耦合”。那么,“高内聚,低耦合”又是为了什么?应对变化,提高复用性!在后文中,笔者还会提到“高内聚,低耦合”

4)设计模式

我们的目的是做出能很好地应对变化,可以高复用的软件,而设计模式便是我们达到这一目的的手段,例如采用工厂方法模式加反射解决应对数据库的变化,适配器模式应对接口不同的类通过适配后协同工作的问题,策略模式应对算法变化,将算法封装在独立的策略类中,GOF的二十几种设计模式,可以说每一种都是应对变化的存在。

说到设计模式,就不得不说设计模式的原则,无论是对开闭原则,单一职责原则,依赖倒转原则,还是里氏代换原则,可以说,没有它们,是不会有设计模式的,设计模式都是在遵循这些原则的基础上发展而来的,再深挖一步,无非就是设计出“高内聚,低耦合”的软件,归根结底是为了什么?满足不断变化的需求!

 

2、面向对象的编程实现

 

1)需求变化是必然的,通常我们可以在一定程度上预测到哪里会发生变化,我们可以采用一些手段(例如上文提到的分层,设计模式等)封装这些变化的地方,从而降低变化带来的影响范围,即新代码可以在影响最低的情况下加入。

 

2)面向对象是通过接口与外界联系的,接口内部与外界无关,从某个角度来说,面向对象的编程也可以看作是面向接口的编程。


笔者通过软件开发方法论的演变阐释了“一切因变而生,顺便者昌”,下面笔者再来举几个例子来支持这一论点:

 

为什么会有文档?

文档是软件开发使用和维护种的必备资料,能提高软件开发的效率,保证软件的质量,而且在软件的使用过程中有指导,帮助,解惑的作用。相对软件开发,后期维护中,文档起到的作用更加关键,是不可或缺的资料。后期维护干什么?主要任务仍然是应对变化,应对变化的前提必须对已有系统足够的了解,文档显然是最直接,最有效的工具。

 

为什么会有命名规范?要有注释?

专业人士和菜鸟的区别有时候也体现在细节方面,类的命名,方法的命名,参数的命名,都有一定规范在里面,例如变量定义,单纯的a,b,c和复杂的_strUserName对计算机来说都是一样的,可是为什么不选择简单的a,b,c呢?写程序不是写完了就完了,还要调试,还要维护等等,时间一久,连你自己都不知道a,b,c到底代表的是什么,更何况如果维护的人不是当初写程序的人,那么这些命名会把人整死的。

同样的道理,注释能使代码更加容易理解,更加容易跟踪。出色的注释就像一幅好的设计蓝图,能够引导阅读者通过你的应用程序的曲折之处,可为后来的维护者带来极大的方便。

大体看,命名规范和注释为了以后阅读程序,修改程序提供方便,同样是为了应对程序变化。

 

为什么会有数据库设计范式?

了解数据库设计的人应该了解,数据库设计范式是为了建立冗余较小、结构合理的关系型数据库。数据冗余,结构合理是为了什么?同样是应对变化,例如:一个《收费系统》的项目,原来的项目的数据库设计是客户信息和会员卡信息放在同一张表,消费记录表同样包含各种客户信息。不难发现客户信息冗余了,采用数据库设计第三范式的设计,则把客户表分离出来单独成表,会员卡信息单独成表,利用外键联系,消费记录中剔除多余的客户信息,只保留客户ID。这样以后,如果客户基本信息发生变化,就不需要更改多张与之关联的表,只更改客户基本信息表即可。那么可以说,遵循合理高度的数据库设计范式的设计能更好地应对变化。

 

如果继续举例子,必然还能发现不少相似之处支持笔者的观点,可是不免有生拉硬扯之嫌,也违背了我写这篇文章的本意。

最后笔者需要指出一点,任何事情都有一个度,一个平衡,盲目地为了应对变化而应对变化,反而会得不偿失,用什么手段促成目的,还要在项目开发中做具体分析。


经常听有些程序员说:编程是一种艺术。我非常赞同这句话,能开发出一款可维护,可复用,可扩展,灵活性好,即能够良好应对变化的软件,的确是一种艺术,更是一种享受,其中必然蕴含着丰富的思想。