软件思考系列之一

软件思考系列之一
邓 辉
12 / 24 / 2004

引言

 人们从事软件开发活动已经有数十年了,和其他许多传统行业相比软件行业还很年轻。为了能够取得和其他行业(比如:建筑行业)比肩的生产力,软件业中有很多有识之士一直没有停止过探索、研究和实践。其中一项影响最为深远的工作就是从传统行业中借鉴过来了“工程”这个隐喻,并希望能够像传统行业那样进行“工程化”生产,以获取满意的软件生产力。然而,“工程”这个隐喻并没有给软件行业带来所期望的结果,甚至还带来了原先没有想到的许多负面影响。Martin Folwer在最近的一篇名为MetaphoricQuestioning 的blog中对此做了详细的阐述,他指出:“和其他行业的活动进行对比,确实可以帮助我们提出问题,但是如果用它来证明方案的正确性就会非常的危险。因此,在我们考虑如何做事情时,应该从思考软件开发过程本身出发,而不应该单纯地以类比作为依据。”

 瀑布模型就是一个典型的“工程”隐喻产物。它试图把软件开发活动分成分析、设计、实现、测试等几个明确的阶段,每个阶段严格地执行本阶段的活动。只有在一个阶段完成后,才能执行下一个阶段的活动。该模型建立在这样一个假设之上:可以在项目开始时把所有需求都明确下来,并且不允许在后期进行大的需求变更。这个假设对于美国国防部的那种不计成本、没有紧迫时间压力的项目来说还可能成立。但是,随着商用软件领域的繁荣,这个假设已经完全无法成立了。因为,变化乃是客户竞争力的重要体现,一个无法快速适应变化的软件是无法为客户带来所期望的商业价值的。瀑布模型也因而退出了主流软件开发过程。

 如今,软件需求的易变性和不可预测性已经成为一个不争的事实。现代的软件开发方法和技术也都是围绕这一点进行的。迭代法和面向对象就是其中的杰出代表。既然软件需求是易变的,那么我们就不再花费力气去阻止它变化,相反我们采用拥抱变化的态度。我们拿到一点需求就快速把它实现,做成一个可以工作的产品交付给客户。客户验收使用后,再给出多一点的需求,我们再快速实现并交付给客户,如此循环迭代。这样做的最大好处是,客户可以尽早拿到一个可以为他们提供商业价值的软件(虽然功能可能不全),并尽快提供真实的反馈,以在下一个迭代中为他们提供最具价值的功能。在这个迭代循环中,有两点我觉得非常重要,一个是CRACK 型on-site客户的参与,还有一个就是能够使软件本身适应这种快速迭代的技术。关于on-site客户,XP资深专家Ron Jeffries有一个绝妙的比喻:“Replacing a on-site customer with some use cases is about as effective as replacing a hug from your Mam with a friendly note.”这个比喻表明了on-site客户巨大的潜在价值。而面向对象技术则是目前使用比较普遍的能使软件适应快速迭代的一种技术。是的,面向对象天生就是一种增量开发技术。

Object-Oriented的目的

在很多书籍和文章中,都把面向对象说成是一种现实世界的建模工具。也就是说,我们可以使用对象来表示问题领域中的实际概念和实体,从而消除了问题领域和软件表示之间的语义鸿沟。在这些书籍中,往往使用很大的篇幅介绍如何使用对象对现实世界建模。我不能说这种做法是完全错误的,但是根据我个人的实践和研究,这种做法是非常不pragmatic的。从这种观点出发建立起来的模型往往是非常幼稚的模型,其中充斥着大量的人为造成的胶合层和混乱的依赖关系网络。原因很简单,面向对象语言是一种通用的语言,它只能描述一些经典的概念。而现实世界中的任何一个概念,在面向对象语言中都是无法精确描述的。比如:简单如“桌子”这样的概念,你能够写出一个类把它准确描述出来吗?试试看。

 另外,就算我们勉强建立起来了针对要当前要解决问题的模型,也会由于前面提到的软件需求的易变性和不可也预测性而使得这个模型变得不再适用。实际上,我们根本不需要做这件事情。我们所需要的是完成目前所需要的功能,并随时准备拥抱变化。面向对象技术在本质上是一种拥抱变化的技术,它拥抱变化的方法是提供了比过程化方法更为强大的信息隐藏和依赖管理手段。通过这些手段,我们可以使软件的粒度和依赖关系更为合理;可以使更多类型的更改局部化;可以尽可能地降低变化所导致更改的代价,从而使频繁更改成为可能。在下一个小节中,我会对如何更为有效地做这项工作给出一些看法。

 那么Generic Programming、Functional Programming以及Aspect-Oriented Programmingn呢?其实它们都是为了拥抱不同类型变化的技术,它们补充了面向对象在拥抱变化方面的很多不足。比如AOP就是为了补充OO在“cross-cut”方面的不足而提出的。在一些论坛上经常会有关于这些不同思维范型的比较和争论,其实这中单纯的优劣比较是非常片面的。在算法领域GP肯定要由于OO,在进行规则描述时FP就强一些,而对于一些系统的基础设施逻辑AOP要略胜一筹。而我们所面对的问题领域基本上不会仅仅是一一种范型的擅长领域。因此我们只用综合使用多个范型,才能最为有效地拥抱变化。

 换句话说,我们想要做的就是要保持软件的“软性” 。这样,我们就可以根据需要把软件塑造成最满足当前需要的形状,并使之逐步演化成一个优雅的产品。而上面所列出的技术其实都是保持软件“软性”的互为补充的手段。如果我们能够从保持软件“软性”的角度出发去学习这些技术的话,相信会从这些技术中获取更大的回报。

顺便说一下,GoF的那本经典的《设计模式》一书中列出的那些模式,实际上都是一些代码软化剂。其中的许多在诸如:Smalltalk、Python和Ruby这样的动态语言中都是一些语言层面的惯用法。而在C++和Java这样的语言中,为了达成一定程度的灵活性(其实就是动态性),就不得不使用这些“技巧”。当然,模式在沟通上的价值还是不能磨灭的。

 两年前我曾经看过Kent Beck先生的《Smalltalk Best Practice Patterns》 一书,当时觉得是本好书,但是理解不深。又经过两年的实践和思考,再回过来回味书中的内容,我觉得这本书是我所读过的讲述面向对象最好的一本书。虽然是以Smalltalk语言为例的,但是书中所给出的OO建议完全适用于其他OO语言,并且比几乎所有专门针对该语言的书都要好。

表达力和Domain-Specific Language

我们掌握了保持软件“软性”的技术,并使得软件非常易用更改,此时我们就满足了吗?不,优秀的程序员都喜欢偷懒,他们不喜欢做一些重复、乏味的工作,虽然这些工作可能非常简单、容易。他们希望能够把主要精力放在真正有挑战性的工作上面。他们希望取得更高的生产力。那么,下一步该怎么做呢?

 计算机语言是一种通用语言,它无法简单、直观、清晰地描述问题领域中的逻辑。问题领域中的一条简单描述,就可能对应大量的实现代码。造成这个问题的根本原因就是因为我们所使用的语言缺乏针对问题领域的表达力。我们需要专门针对这个领域的语言:Domain-Specific Language。

 前面提到过,试图使用计算机语言去直接建立问题领域的模型,其结果往往是一个生硬的模型,在变化面前根本不堪一击。因此,比较好的做法是:不要去针对当前的问题去构建一个具体的架构,而是要对要解决的问题进行深入的理解,逐渐形成一个抽象、柔性的隐喻。并以此隐喻为指导,逐步构建起一套针对该领域的特定描述语言。我们不再是努力去构造针对手边要解决问题的模型,而是去构造一些能够直观地描述问题领域中问题的一些单词、短语和一些语法要素。基于它们构建起来的解决方案往往更加直观、简洁,并且更容易包容变化。这里要特别提出的是,隐喻听起来往往给人一种不正式和不切实际的感觉,但是从我个人的实践来看,一个好的隐喻的指导意义要大大超过那些具有正式定义的所谓的架构。

在构造这个领域特定语言的过程中,前面提到过的那些保持软件软性的技术(OO、GP、FP、AOP)非常重要。因为,这个过程也是一个频繁迭代的过程,只有在大量的快速迭代之中,才能不断地获得反馈;才能不断地调整自己的理解;才能不断地完善隐喻;才能逐步形成一套直观且富有表达力的DSL。而这种不断的演化必须要以能够保持软件软性的技术为基础,否则代价是及其昂贵的。

以这种方式构建系统,还有两个很好的副产品:重用和生产力。因为这套DSL完全可以应用在同一个领域的其他问题中,并且再次得到完善。同时,表达力的提高必然带来生产力的大幅提升。

 在自底向上 构建DSL方面,到目前为止,我看到的最好的图书是Paul Graham的《On Lisp》 。其中对这种技术进行了深刻而富有启发性的阐述,并且具有大量的实例。不过,书中使用的语言是Lisp。同样地,我也非常强烈的推荐大家去学习Lisp语言,因为在Lisp中不仅蕴涵着大量关于计算机科学本质的思考,并且它还是一门非常优美的语言。相信您学完之后会有很多意想不到的收获。

(待续)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值