Hello, world! — 我心中的道
序言
08年6月,地震之后,在家无事,下载Sun上的Java Tutorial开始了自己的Java之旅。之前看过K&R《C语言程序设计》,对编程算是有一点点基本的认识。而后数月,无意间碰到设计模式,也许是无知者无畏,没有多少编程基础的我又开始了学习设计模式,这之后,便搜索到了Jdon等关注设计模式的网站,尽管我学设计模式不是从Jdon这里学的,但Jdon无疑给了我一点暗示:我正在学的东西,多少有点用处,反正也没什么事,就学学吧。当然除此之外,Jdon还给了我两个非常重要的提示:四色原型和领域驱动设计。一年多来在Jdon上没有发过帖子(除了前段时间跟了banq的一个帖子),而如今工作也有数月,对软件开发有一点点粗浅的认识,在此写下,算是对Jdon的感谢吧。
1、何为程序:数据结构、算法与设计模式
程序 = 数据结构 + 算法,自从Niklaus Wirth提出这个公式以来,也看到一些人其进行修改或者扩充,比如修改为程序 = 对象 + 消息,比如扩充为程序 = 数据结构 + 算法 + 方法,程序 = 对象+ 消息 + 软件结构等。在这里,我也提出了“程序 = 数据结构 + 算法 + 设计模式”,下面进行解释。
数据结构的重点在于存储,即将如何存储数据:这里,我不想将数据结构仅仅理解为表、树、图,对我来说变量、对象等凡是可以容纳数据的东西,都可以理解为数据结构。
算法的重点在计算,即如何处理数据:这里我也不想将算法仅仅理解为排序、检索或者更复杂的如路由、图像处理、机器学习算法等等,对我来说函数调用、消息发送等处理信息的方法,都可以理解为算法。
不同于数据结构与算法形成“剑”之双刃之“利”,设计模式的重点在“剑”之“用”,即考虑为何及其如何封装某些算法和数据结构以支持需求的变化?[注:这里且把“程序”喻为“剑”,正如一把好剑、一身剑术可以成就一位剑客,这一“利”一“用”,也可以成就一名programmer。如果这个比喻妨碍你的思考,请放弃。]
上面将“程序”的“数据结构与算法”喻为”剑之“利”,将“程序”的“设计模式”喻为“剑”之“用”,只是为了形象化突出两者不同的一面。事实上,这个比喻也有它的弊病,它妨碍我们理解其相通之处,甚至限制了我们的思考,所以下面我尝试放弃这个比喻,以解释它们的相通之处。
其一,程序 = 数据结构 + 算法 + 设计模式,这个公式,从它们各自思考的侧重点,可以进一步推广为:程序 = 做什么 + 怎么做 + 为什么。注意“侧重点”这个词,因为事实上设计数据结构,也会考虑到怎么做和为什么?设计算法时,也会考虑到做什么和为什么?在设计模式中,同样也会考虑到做什么和怎么做?强调侧重点,就是要表明它们只是在“核心关注点”有所不同。
其二,有的人说,设计模式是站在比数据结构与算法更高的层次思考问题。事实上,也经常是如此。不过,要注意到结构型和行为型设计模式的价值除了支持变化,还有很重要的价值,就是形成更大的数据结构和算法。你是要说“设计模式”这个盒子封装了许多“数据结构和算法”,还是“数据结构和算法”这个盒子封装了许多“设计模式”呢?两者都对,也都不完全对。“Thinking out of box”,只是相对的关系,当我们从井底跳出井外,以为不再是井底之蛙,井外可能不过更大的一口井而已,而且可能是一口枯井,还不如跳回去,如鱼得水呢。此外,“一沙一世界、一花一天堂”,如能以小见大,即便"Thinking in box",亦可悟道。
当然,解释了这么多其相通之处,意也不在抹杀其差异。虽然他们都涉及到“做什么,怎么做,为什么”,在具体的情境中,核心的关注点还是有所不同的;虽然抽象“其大无外,其小无内”,在具体的情境里,抽象的层次还是有相对高低之别的。
个人对数据结构、算法和设计模式是交替学习的,但对设计模式更倾心。一是设计模式的数量很少,你看看单单一个字符串匹配就有成十上百种算法,机器学习算法更是举不胜举,数据结构好像少一些,但比起设计模式也不算少;二是设计模式更简单,一般说来,理解一种算法和数据结构的构造的时间不会比领悟一个设计模式的时间更少;三是设计模式更通用,不能否认有些算法和数据结构也是广为使用,比如哈希表和快速排序法,有些设计模式,比如解释器,也不够通用,但整体上说,设计模式是更通用的,举些简单的例子,如Java中的collection内置了迭代器模式,io内置了修饰器模式,而collection和io基本上是在所有的程序中都要用到的,理解了设计模式,就能以举一反三,更快更好地掌握它们;再比如常听见的MVC某种意义上可以理解为观察者模式和中介者模式的合成,IoC可以理解为运用反射机制的工厂模式等等。
学更少、更简单的东西,却有更多、更大的用处。这可真谓"Less is More",何乐不为呢?当然数据结构和算法,也不能不学,如Donald E.Knuth《计算机程序设计艺术》第三卷的排序和检索,Josh Bloch的集合框架等等还是值得系统学一学的,原因也是上面三点:少、易、通用,当然这三点也是相对浩繁的算法和数据结构来说的,此外,数据结构和算法中也可能隐藏着和设计模式同样优秀的思想。但吾生有崖,生命太短,技艺太长,我们只能先选择很小的一部分东西进行深入研究作为自己的认知基础,其余的只能根据客观要求或主观需求临阵磨枪,随机应战了;也许某天遇到某些东西我们极其感兴趣,那不妨也就呆在那里不走了,管他春夏与秋冬,躲进小楼成一统,自得其乐的生活也是令人向往的。
2、何为模式:分析、设计与实现
模式,简单说,就是反复出现的情景,在分析、设计与实现的阶段都存在着。上节可以说是从程序本身的“构成”来看“设计模式”,但这节从程序开发的“过程”来看设计模式,它在开发链中前接“分析模式”后续“实现模式”,因其广为人知,所以先说说。
谈及设计模式,不能不说其那本书《设计模式:可复用面向对象软件的基础》,从该书的书名的副标题可以看出“复用”二字,即设计模式之目的。但如何复用呢?对于面向对象,一般的书上都会提到四个最基本的概念:抽象、封装、继承和多态。设计模式的复用技术,本质上只是这四个概念的运用而已:通过“抽象”将变化与不变的部分进行分离,用接口“封装”固化不变的部分,用“继承”释放变化的部分,即“多态”。其中接口可用抽象类替代,相应地继承也有“黑箱”(即接口实现)和“白箱”继承两种。
GoF这本书个人认为最大的贡献在于两点:1)对模式的命名,2)对模式的分类。看起来没什么,但合适的命名和分类真的不太容易,即便是科学家或哲学家,对事物进行命名与分类,他们也要花费大量的精力和心血。模式的命名约定成俗,深入人心,一般难以修改;但模式的分类,却可能被后来人进行修改或完善。
曾听人说,哲学的根本问题有两个:生与死,灵与肉。GoF将设计模式分为创建型、结构型、行为型,与此颇有相似之处。不妨将“生”对应“创建型”,将“肉”对应“结构型”,将“灵”对应与“行为型”,“死”呢?怎么没有?孔子说:未知生,焉知死?试图回避死这一问题,GoF也未考虑对象之“死”。在Java世界中,GC算法是不是可以对应“死”呢?其模式倒是简单明了:“无用”即“死”。我支持GoF的这种分类,而对于Bruce Eckel在著作《Thinking in Patterns》打破这种分类,犹如看到一些人解读《老子》一书,随意重新组合章节,甚至道与德倒置,是保留不同意见的。个人建议可以按照自己的理解在保留GoF的分类框架进一步细分,发现各种模式的不同点(正交性),以便于在需要时对多个模式进行组合使用。有一篇文章《Relations between Design Patterns》主要关注各个模式的关联,可以参考。所有模式的共性不过是四个OO基本概念的运用,因此找出各个模式的个性(关键不同点)才是剩下值得去做的事情。看过一些评论说GoF这本书不好懂,但个人认为学习设计模式还是以其为中心,如某个模式看不懂,可采取迂回策略,先看看相关的资料,再回来看GoF这本书。
不同于设计模式的关注点在如何设计模型以支持需求的变化,分析模式的关注点如何将需求转化为模型。Martin Fowler的《分析模式--可复用的对象模型》、Peter Coad的《对象模型--策略、模式、应用》等等关于分析模式的著作,都尚未拜读,加上经验实在匮乏,所以本不想说。可是对于Peter Coad的《Java Modeling in Color with UML》一书中的四色模型,一见如故,极其简单的四色模型,在我心里已经将其作为自己关于分析模式的核心认知,这里说说自己对四色原型的理解(再次感谢Jdon让我遇见了四色模型)。
四色模型中有四个最核心的概念(刚好设计模式也是运用了OO的四个基本概念,无巧不成书呀),分别是MI(Moment Interval)、Role、PPT(Party Place Thing)、Desc(Description), 不同设计模式运用的OO的Abstraction、Encapsulation、Inheritance、Polymorphism四个基本概念在中文有很好的翻译:抽象、封装、继承、多态。这四个词,直译起来,似乎没什么感觉:瞬间-间隔时间、角色、聚会场所-物件、描述。查阅一些资料,其描述我不是十分满意,这里我尝试用简短的言语解释其本质:MI描述活动,Role代表参与活动的事物;PPT代表未参与活动的事物,Desc描述事物。举个例子,比如一件商品,商品本身就是PPT,商品的类别信息、厂家等商品的元信息就是Desc,如果这件商品为顾客所购买,此件商品就变成了Role(当然顾客此时也是Role),而顾客购买商品这个活动就是MI。
世界可以简单理解为由事物及其运动(MI)构成,事物又有动静之分,动即参与活动之事物(Role),静即未参与活动之事物(PPT),此外,可能还需要描述事物的信息(Desc)。为了将原型形象化,我们可以根据色彩的冷暖动静之感,将活动(MI)涂上活跃的红色,将参与活动的事物(Role)涂以暖色黄色,将未参于活动的事物(PPT)涂以安静的绿色,将描述事物的信息(Desc)涂以冷色蓝色。这样四色原型就诞生了,通过四色原型,我们可以像孩子拼接积木一样,一块一块地拼接出需求模型的原型了。
对于《Java Modeling in Color with UML》这本书,我想模仿Gof的书名《Design Patterns: Elements of Reusable Object-Oriented Software》,给其取个别名,以彰显其价值。《Analysis Patterns: Make Requirement Prototype Dynamics》,中文名可以翻译为《分析模式:构建需求原型动力学》或者《分析模式:动态构建需求原型》。其中副标题的首字母对应四色原型的MI、Role、PPT、Desc四个词的首字母。
在说实现模式之前,稍微总结一下设计模式与分析模式的异同点。复用是模式共有的特点,但设计为支持变化的目标,往往采用“间接”的手段来实现,以增强其灵活性;而分析则为了真实地将需求映射为模型,手段越“直接”越好,简单直接的方式,更能刻画真实的需求。如上面所述,它们各自有一本极好的书来描述它们,都只有六章,都很薄,可反复阅读。
分析模式将需求转化为原型,设计模式支持需求模型的变化,而实现模式为何而生?实现模式关注的是代码本身,我们在分析和设计上付出的努力,终究要落实在代码上。分析和设计再好的模型,也可能被充满smell的代码掩盖或破坏。Kent Beck在《实现模式》一书提出了77个实现模式,6条原则和3种价值观。77个实现模式涉及一些具体的代码规范的建议,暂且不论;6条原则,局部化影响、最小化重复、将逻辑与数据捆绑、对称性、声明式表达和变化率,也暂且不议;3种价值观:沟通、简单、灵活,我也只想说说其中的一点:沟通,因为个人认为这是实现模式最根本的意义所在。Kent Beck在书中说:过了20年,把别人当作空气一样的编程方式才在我眼中褪尽了颜色。作为一位programmer,代码的作用不仅在于与机器交流,更重要的是与别的programmer交流,将“沟通”的价值观贯彻到代码的编写中,是值得每个programmer坚持去做的事情。Kent Beck的这本也极其薄,可以翻翻。
上面所说的三本很薄的书,可以作为关于分析、设计、实现模式的认知基础,值得收藏。
3、何为软件:架构及框架
从简单意义上讲,软件和程序之间基本可以划上等号,但软件还考虑到开发、维护的时间与金钱等诸多现实因素,经常使用各种框架(SSH, Jersey/Guice/Ibatis等)以节约开发时间,应用项目管理(Maven)、版本控制(SVN)、日志记录(Log4j)等以规范开发流程等。
在组织各种框架时,需要考虑框架之间如何集成,由什么作为中心向边界扩张。比如领域驱动设计,就是以领域为中心,暂不考虑界面,不考虑持久化,试图在不受外在技术的干扰下,认识需求的本质,并寻找合适的概念去表达。至于用户体验(界面)和性能(持久化),虽然也极其重要,但最好还是在认识领域的本质之后再考虑。软件开发除了考虑从哪里入手(比如领域驱动),还要考虑如何保证每一步都走得正确(比如测试驱动)等。
架构试图在整体上把握软件的各个层次与部件,而框架则试图从不同的层次与切面支撑着架构。框架本身则由程序的集合构成,可以说包含了设计模式、数据结构与算法三个成分,分析、设计、实现三个过程。这样看来,似乎软件考虑的更多是宏观问题—如架构与框架,而程序考虑的则是微观问题-模式、数据结构、算法等,有点粗糙,姑且这么说吧。
那么到底什么是软件?什么是软件的灵魂?这应该是一个众说纷纭的问题。 就像我们要偶尔灵魂出窍,看看自己这副臭皮囊除了吃喝拉撒,还能干什么?我们也要让软件的“魂”偶尔出窍,以接近软件的真相。和对程序的描述一样,Niklaus Wirth对软件的描述同样令人印象深刻:一切可以是软件,但软件不是一切。
道可道:走为上
道可道,来自《老子》第一句话,走为上,来自《孙子》的最后一计。而我将二者贯穿起来,只是想表达一个想法:道是可以走的。仔细说来,道并非不可言说,不可实践,相反道是可以言说,可以实践的,只是可能不那么容易而已。顺道而行,我们应该可以节约一点点时间的。将自己的想法写于此,也希望能有助于加快后来者前进的步伐。这就权作结束语吧。
联系方式:AchieveIdea@gmail.com
[该贴被jdon007于2010-10-25 00:01修改过]