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修改过]

2010年10月24日 23:52 "jdon007"的内容
程序 = 数据结构 + 算法 + 设计模式 ...

这···其实单从程序,软件的定义,只要动动脑筋,定义也很难有错。但我们在面向对象和面向过程上考虑的话,可以发现两者的切入点(思想)是不一致的,所以不应该把两种不一致的思想合起来去描述一样东西。要么你就用底层的思维去描述,要么你就用OO思维去描述。混起来只会四不像。

而关于算法和数据结构,还是以公认定义为好,否则很难交流。(自己理解可以,但别把这些作为描述公认的定义,自己一套,大众一套,肯定会出入的。当然,若果你是怀疑或者否定公认定义,也就另外一回事了)

2010年10月24日 23:52 "jdon007"的内容

不同于数据结构与算法形成“剑”之双刃之“利”,设计模式的重点在“剑”之“用”,即考虑为何及其如何封装某些算法和数据结构以支持需求的变化? ...

不敢认同,软件开发是一个“造”的过程,“利”与“用”都没有对“造”进行剖析,所以不应该从那个角度去理解。个人认为,数据结构与算法只是“剑”之“材料”,OO是“造剑”的世界观,而分析设计思想则是“造剑”的指导思想,至于造的“剑”如何用,则是对软件如何使用而已,与造无关。最后一句和我思维有出入。设计模式的切入点根本不再封装这个词上,也不在算法和数据结构上。封装是OO思维所带入的,在使用OO思维时就已经存在,即考虑一个类的建立。设计模式是思想,某种代码只是一种表现形式而已。

设计模式中的某个模式,只是针对某个情况下设计的解决方案,不是考虑其通用不通用的问题,他和具体业务没有任何关系,它的身份相当于“公式”,当用它能解决问题时就用,不能用就不能用,而且设计模式就是从大多设计中提取出来的,没必要考虑其通用性。

关于设计模式的定位,设计模式是一种设计思想,在OO世界里思考单位是对象,设计模式也不例外,所以在谈论设计模式时,却引入算法和数据结构是不科学的。举个不恰当的比喻吧:在社会交往中,我们考虑的单位是人,不是人的内脏和构成。(一个角度,一套认识。若果你硬要考虑“内脏”,我也没办法)

至于其他方面,大概上和LZ差不多吧,感谢交流。
[该贴被SpeedVan于2010-10-25 12:07修改过]

其实我关于数据结构与算法的定义,应该还是符合Niklaus Wirth的原意的,可以找找Niklaus Wirth的访谈录看看。此外,Niklaus Wirth认为面向对象与过程化并本没有本质的不同,这在他的访谈录中也说到了。与面向对象、面向过程(命令式语言)本质不同的应该是函数式、面向进程、逻辑式等类型的语言(声明式语言)。正是出于对前人的尊重,所以我才没有去随意改造他们提出的概念,不然我会直接按常识说:程序 = 做什么 + 怎么做 + 为什么。在GoF书中,他们也说设计模式本身并不难,难得只是恰到好处地应用,即要考虑“为什么”的问题。

比喻只是比喻而已,放弃本体以喻体来讨论,不同的比喻方式,可能会出现偏差或冲突,所以这里就暂且不再用比喻说了。

个人认为OO并没有引入什么了不起的思想,它只是承载了一些了不起却十分平常的思想。比如抽象,任何一种语言都是某种角度对现实世界的模拟和抽象;比如,封装,过程化中就没有封装了吗?只是封装的方式不同罢了。至于设计模式的切入点,这个还真可能是封装,Dennis Ritchie 曾很中肯地说,“OO是一种很好的想法,通过界面来隐藏具体的实现细节。可是它可能有点过火了,一切都是界面,可能很难看到程序本质的终结”。如果没记错,针对接口(界面)编程,在Gof书中应该是列为第一原则吧。

不谈设计,只谈模式,模式如果不能复用,也就是说如果它不具备一定的通用性,就没什么特别的价值了。当然每个模式,都有其具体的应用场景,但这个具体并不是说它不可以反反复复、经常地使用用吧。

说到这,关于设计模式的定位,在文中我从程序本身的构成,与程序开发的过程,对其进行“静态”与“动态”的定位,并没有觉得不妥。

至于讨论设计模式时,要不要讨论算法与数据结构,Gof在书中说“结构型模式涉及到如何组合类和对象以获得更大的结构”,“行为模式涉及到算法和对象间职责的分配”。在文中我已从“关注点”和“抽象层次”两个方面论及两者的相通与相异之处,就不再复述了。

[该贴被jdon007于2010-10-25 14:53修改过]

先说清楚的是,面向对象和面向过程是思想,只有当出现具体规则时,才会成为语言,类似java,c#。思想归思想,实现归实现。

思想之间肯定有着区别,面向对象和面向过程,是一种世界观,他们都是对世界的一种认识方式。但是他们的“切入点”是不同的,一个对象,一个是基本类型。其实换句话说,面向对象的单元更粗粒度。若果你在用算法和数据结构,你已经不是用OO思维来思考了。所以在谈OO的时候,明显不能加入数据结构和算法等内容了。

若果你谈到他们的相似性,的确他们很相似,因为本来他们就是对同一个世界,不同粒度的认识。
我以前举了一个例子:树形。

树形:客观实在;
数据结构中的树形(树):面向过程;
设计模式中的树形(组合模式):面向对象;

千万别以为最根本是数据结构的树,若果那样想的话,就可能出现感到面向对象依赖面向过程。实质上,他们的交集只是客观实在(树形)。主要原因在于封装这一概念,对于对象的内部,我们是不可以直接获得,只能够通过方法。而加上面向对象的最小单元是对象,所以在面向对象世界观中,算法和数据结构这些已经抛离(不能向下了),而加入来的是设计模式和对象集概念(不知道能不能对应起来,反正就是相似但不一样的东西)。例如若果你用面向过程语言实现面向对象(C++例子),那么你现在就是用OO了,而非面向过程了,只不过,是不纯的OO罢了。

>>结构型模式涉及到如何组合类和对象以获得更大的结构

“结构型模式”是存在于面向对象的,“结构”是指客观世界的结构,这两者都不是指数据结构。

>>行为模式涉及到算法和对象间职责的分配

这里的算法和对象间的职责只是一个词,可以理解为方法,因为使用时根本不会去考虑其如何完成的。

当然当中的算法可以自己写,但写算法的时候,并不是在用OO思维思考问题,只是为OO提供支持而已(类似API、动态链接库一类的技术支持)

最后:
当你理解1+1=2;对于面向对象来说是错误的;那么你就理解OO思维为什么不同于面向过程了。

(numOne.add(numOne)).equalObject(numTwo);所以为什么说java是不纯的OO语言,也就这个道理了。

两个世界观的尽管相似,但不是相等的东西。这是我对OO和面向过程这两个思想,最为根本的理解。

(对于我说的,OO是一种世界观,这仅以交流)

[该贴被SpeedVan于2010-10-25 16:56修改过]

Niklaus Wirth 认为面向过程与面向对象的主要术语是同义词:

variable object
procedure method
parameter message
calling a procedure sending a message
data type object class

将程序=数据结构+算法,这一公式用在OO和PO两种编程范式上,可以得到,面向对象 = 对象 + 发送消息;面向过程 = 变量 + 调用过程。如果对对象、变量的理解不过于僵化的话,应该可以看出它们的一致性。主要的差异是,对象将变量与过程捆绑在一块了,致使调用过程与发送消息的形式不同,但这并非本质性的差异,它们都属于“命令式编程范式”,真正与它们“水火不容”的是“声明式编程范式”。当然,“命令式”与“声明式”虽水火不容,还是存在共性的,那就是程序的本质。这属于另外的话题,就不说了。

调用过程时,也可以不考虑过程怎么实现,就像发送消息时,不考虑消息怎么样被处理。

还有经常听人说(教科书)的按照纯粹的面向对象方法,x+y,必须表示为x.add(y) 。在访谈录中Niklaus Wirth对此不以为然。至于我的理解呢,发送消息的形式,何必如何僵化呢?x + y,为什么就不能理解为x向y发送了+的信息呢?举个生动点的例子,你对一美女说:我们相恋吧!按照面向对象,发送消息的形式似乎要这样: 美女.相恋(我)。而“我们相恋吧”这种形式的消息表示难道不比上种形式的消息更自然呢?当然在语法上强制保持消息形式的一致性,也是有好处的。这只是“形而下”的区别,而绑定带来的一些衍生的语言特性,也非常有用或有趣,但这个还是改变不了它们在本质上的一致。

在数据结构与算法,加上“设计模式”这一个东西,是考虑如何使用算法和数据结构,在面向对象中即如何组织对象、分发消息;在面向过程中即如何构造变量,调用过程等;考虑的是为何及其如何组织的问题。

再次说明,我对数据结构与算法的定义虽略显自由,但并没有脱离其根本含义。在文中已从“关注点”和“抽象层次”进行了解释。对于习惯OO的人来说,也许将程序定义为,程序 = 对象 + 发送消息 + 模式,其中模式考虑为何及如何组织对象与分发消息,更容易被接受。
[该贴被jdon007于2010-10-25 18:43修改过]

2010年10月25日 18:40 "jdon007"的内容
程序 = 对象 + 发送消息 + 模式 ...

我看了你的描述,我懂你意思了,但从我回复中可以看出,我并没有对他们在具体实现上对他们进行区别(变量,过程等),只是针对思维(世界观),我只是指出即使OO与面向过程再怎么相似,再怎么相近,也是两套对客观世界的认识(注:不是对程序的认识)。例如即使没有了任何一个面向对象语言,我一样可以OO来认识和理解客观世界。

而对于同一样事物,我不赞同结合两种世界观去描述。因为那样很难交流起来,除非两个人都认为这两套世界观是一个(这样的想法不知道有多少人呢?至少我不是)。OO就OO,面向过程就面向过程。尽管你揭开了OO的面纱看到了过程,但他们就是这么不一样的两套的世界观。至于变量与过程这些,是用计算机上的语言,对这套世界观的描述而已,并不是世界观本身。

OO中间没有变量和过程的定义,为什么OO这么接近与哲学,也许就是因为这样;为什么说OO更接近人类对客观世界的认识,可能也是这样的原因——OO本来就是一种接近人类认识的世界观

正如你所说:“对于习惯OO的人来说,也许将程序定义为,程序 = 对象 + 发送消息 + 模式,其中模式考虑为何及如何组织对象与分发消息,更容易被接受。”

若果只是对于本质的认识上,我没有作出任何反驳,但是在描述上却表示不同意而已。若果是以“数据结构 + 算法 + 设计模式”来写软件,到底是开发一个什么软件来呢?难道要回到那个不完全面向对象的年代╮(╯▽╰)╭。所以之前我也说了在描述事物时,要么OO,要么面向过程。

关于美女那个比喻,“+”到底指谁的发送消息的方法呢?谁收的消息呢?没可能自然而生吧,消息谁到谁?当然你可以认为前者发给后者。那发送是谁负责?当然你可以认为是前者,但这样以后,和add有区别么?(当然可以做成EDA,但只是一个选择而已)至于分给谁这又是一个职责问题,不作讨论。(你那样的比喻若果一开始就合理的话,只要我们能刻意不用int这些基本类型,那么Java就纯了)

Gilad Bracha在他博客中写道:“我经常说,Java的原罪在于它不是一个纯面向对象的语言(一切皆对象的语言)。”文中讨论的主要内容不是纯面向对象语言的优点,而是提出了一个问题:Java如果没有原生数据类型,是否能保持高性能?答案是肯定的。(引用自:http://www.infoq.com/cn/news/2009/06/java-without-primitives)

为什么追求更OO,为什么scala被提名为java皇位的继承人,也就是这原因(还好,我爱的java还没死,但我也看好scala)。
[该贴被SpeedVan于2010-10-25 22:12修改过]

好,那我就从世界观说说它们的相通之处,这有点过于“务虚”了,对习惯“务实”的我们还真的有点不太适应。

两种范式都要描述“事物及事物之间如何作用”这一普遍画面吧。
现在有x事物与y事物,它们之间发生了某种作用,称之为f.

用面向对象的思想,是将f放在x或y事物之中,比如将f与x绑定。这样我们用对象x与对象y描述这两种事物,用x.f(y)描述两种事物之间的相互作用。

用面向过程的思想,并不将f放入任何事物之中,f不与x或y绑定。这样我们用变量x与变量y描述这两种事物,用f(x,y)描述这两种事物之间的相互作用。

对我来说,消息与过程只是描述事物相互作用的一种方式而已,在某些情况下面向对象更自然,在某些情况下面向过程更自然。

举些例子,比如描述“猫吃老鼠”这个画面,用面向对象的点语法描述更自然,猫.吃(老鼠),此时面向过程则不太自然,吃(猫,老鼠)。而描述“猫与老鼠相爱”这个画面,用面向过程的语法描述更自然,相爱(猫,老鼠),而此时用面向对象描述,猫.相爱(老鼠)或老鼠.相爱(猫)似乎都可以,但将相爱这一行为放在猫还是老鼠中,都不太自然,因为这一行为是由猫与老鼠共有的,不适合绑定某一具体的对象中。

在刻画“事物与事物之间相互作用的画面”时,很多时候适合将“作用”绑定在某个“事物”之中,这就是“面向对象”的做法,而也有些时候“作用”本身并不属于某个“事物”,此时面向过程就更为自然。比如java中math库,都是一些静态方法,其实就是因“数”与“数”之间发生“运算”,而“运算”这一“作用”并不适合绑定在某个“数”(事物)上,所以用过程化的表达则更自然。

事物与作用,这些在生活中浅显而深刻的语言,应该说不需要另外开辟一些对应的术语来描述,可是可能是为了对外行建立技术“壁垒”,也可能还是为了便于内行的“交流”,反正前人发明了与之等价的术语,数据结构与算法。我只是套用了这些术语表达我的程序观。模式则用来描述了“事物及事物之间相互作用,经常出现的一些情景”,因为经常出现,所以模式也可以称之为规则或规律,可加以运用。

“程序 = 数据结构 + 算法 + 模式”,是用程序的术语描述“世界 = 事物 + 作用 + 规律 = what + how + why”。而“程序 = 变量 + 调用过程 + 模式”,“程序 = 对象 + 发送消息 + 模式”,只是不同语言范式中对程序本质的不同描述,描述的方式可能略有差异,但“本是同根生”。它们都可以用来描述一切画面,只是在有些画面用“面向对象”更合适,在有些画面有“面向对象”更合适而已,虽有不同,但不至于“水火不容”。

真正与它们水火不容的是,“函数式、逻辑式等范式”,在这些范式中,并不是以“事物”为中心,而是以“作用”(比如函数、逻辑)为中心,“事物”在这些声明式的范式中,不过是“过眼云烟”,而在面向过程和面向对象这些命令式的范式中,“事物”是主体,“作用”只是描述事物之间如何相互作用而已。

呵呵,楼主其实在做一种尝试,试图将函数式、逻辑式等范式和对象范式进行比较或者认为各有侧重。SpeedVan认为是不能混合。

不知我的理解对不对,楼主这种想法老外也有很多人在尝试,比如有人认为面向函数式语言就是侧重计算过程的,所以,侧重过程和侧重事物对象封装应该说是两种流派,一直在争论,我们应该直视这个现状。

这两个流派之所以争执,因为都有一种试图将对方包含为自己的意图,比如将对象看为数据,也是楼主的一部分意思,反方认为,对象包含数据,数据是静止的,对象不但包含静止的数据,还包括这些数据活动的动作和算法规则,所以,可以把面向对象看成是一种包装方法论,类似吸功大法。

当然,把对象看成数据的人,也会用数据看成吸功大法,万物皆数据啊。

所以,这类似两种宗教争执,有先入为主概念。具体我想谈谈两个公式的区别:
程序=数据+算法+模式
程序=对象 + 消息
这两个公式其实从不同角度来看程序的,不同角度就决定不同应用场景,比如前者可能更适合做基础组件 算法 通用功能 数学研究的领域;后者更侧重一线应用领域。

正是由于有角度不同,所以才有对scala的争议,现在有人说scala复杂,有人说不复杂,适合优秀程序员,其实就是这两种方式哪个更适合人的自觉(这个自觉是不经过数学严格教育),很显然Java更适合,所以,现在才有初中生经过培训机构出来也能编写Java。

以上只是个人意见,仅供参考。

banq的理解可能与我的解释有所出入,这可能是语言的局限,我用这种“语言”描述我心中的“思想”,在别人看来这种描述可能是另一种“思想”。这个确实是“仁者见仁,智者见智”的问题,真相是否能在讨论中呈现出来,不得而知,我们只能尽力而为。

世界简单说由“事物”与“运动”组成,“事物”与“运动”会呈现一些普遍现象,其背后的原因可以称之为“规律”。“事物、运动、规律”这三个概念虽然难以精确定义,但希望我们对其的认知是基本一致的,否则接下来的讨论,可能会出现混乱的情景。

我说面向过程(以C为例)与面向对象(以Java为例)的世界观,本质上并没有不同,因为他们都认为“世界是由事物组成的,运动改变着事物的状态”。至于“事物”是用变量表示,还是用对象表示,至于“运动”用过程调用表示,还是用消息传递表示,这只是“方法论”的不同而已。

而我说的函数式(以Lisp为例)与逻辑式(以Prolog为例)的世界观,本质上也没有不同,因为他们都是认为“世界由运动组成,事物改变运动的轨迹”。至于“事物”是用数据表示,还是用条件表示,至于“运动”是函数表示,还是用逻辑表示,这也只是“方法论”的不同而已。

我沿用前人的术语,但不会为其术语所困扰,原以为会便于交流,不幸的是带来误解。“像外行一样思考”,用生活化的语言进行思考,和外行用生活化的语言与其交流,是我的习惯,只是生活化的语言可能会被“专业人士”认为“不专业”。我不是否认“术语”的重要性,只是当“术语”碍手碍脚时,不妨“壮士断腕”,“清空大脑”,回归生活与自然进行思考。

下面回到程序上来,沿用Niklaus Wirth对程序的思考,程序 = 数据结构 + 算法,我以为“数据结构”就是“事物”这一个概念在程序世界的最高“抽象”,“算法”就是“运动”这一个概念在程序世界的最高“抽象”,“模式”就是“规律”这一概念在程序世界的最高“抽象”。所以,才认为“程序 = 数据结构 + 算法 + 模式”。

至于在“命令式”(世界观,运动改变事物的状态,计算改变数据的状态)中,面向过程和面向对象的方法论,有所不同,但这不能说其世界观本质不同;而在“声明式”(世界观,事物改变运动的轨迹,数据驱动计算的运行)中,函数式与逻辑式的方法论,有所不同,同样这不能说其世界观本质不同。

我相信Niklaus Wirth心中对数据结构与算法的定义不会那么僵化。作为一位图灵将获得者,作为在软件与硬件领域都有杰出贡献的人,我很难相信,他对程序本质的认识,仅仅局限于“过程化”。所以,出于敬意,在表达自己的程序观时,我使用了他定义的“术语”。

为了更完整地表达世界的本来面目,在“事物”、“运动”这两个概念上,添加了“规律”这一概念;相应地为了表达程序的本来面目,在“数据结构、“算法”这两个术语上,添加了“模式”这一“术语”。将这一“公式”,用在过程化则是,程序 = 变量 + 过程调用 + 模块(模式),用在面向对象,程序 = 对象 + 消息传递 + 模式。

至于banq说的数学领域与应用领域的区别。一个以“利”为中心,一个以“用”中心,对“程序 =数据结构 + 算法 + 模式”的关注点略有不同;数学领域关注的是“数据结构与算法”之“利”,应用领域则关注“模式”之“用”。在文中,我已从“关注点”(横向)和“抽象层次”(纵向)解释了其相通与相异之处,banq过分强调其对立性,似乎banq曾说“要是谁再在jdon上说程序=数据结构+算法,就再骂他一次”,希望这次banq能“刀下留人”呀。
[该贴被jdon007于2010-10-26 13:58修改过]

表明立场:
我没有反对以过程的思想去看软件,甚至我同意以对比的方式去认知。(别对我会错意哦╮(╯▽╰)╭)
而且本来两个角度都有自己的优点和弊端。

但我只是想表达:换角度可以,但不要换一半而不换一半,设计模式本来的出发点就是针对OO上的设计,因为他们都是遵守着5大原则。数据结构和算法如何去运用设计模式来体现5大原则呢?数据结构和算法不具备OO的四大特征,若果用面向过程的方式来表达设计模式,我真不知道如何表达。(你要是做出这些特征,很好,你大概已经开发一门OO语言)

variable包含着(variable,procedure),variable1与variable2如何抽象出variable3?

还是那句话:尽管他们有很多相似的东西,但仍然是不同的东西。

可能在面向过程里面有更设计模式相似的思想,但那不是设计模式。

对于你说职责不在两个对象身上的时候,OO上有关于这种情况下的设计——DCI,所以说尽管相似,但不是一样的东西。换角度请换得彻底。

若果“程序=数据+算法+模式”中的模式不是设计模式的话,没问题。我可以理解为“PO中的模式 相对于 OO中的设计模式”,但混起来就风马牛不相及。在软件中设计模式是用来使用的,必须联系在OO环境下。单独思考模式的内在和本质没问题(thinking in box),“程序=数据+算法+设计模式”,单从上下文就是不正确的。单独思考设计模式时可以Thinking in box,但在有联系的思考时请保持上下文一致——Thinking out box。而我反对LZ的观点,大概就在这里的了。(关键词:上下文)

环境是OO软件设计中的思考因数,只在某个环境下才能做某样事情(DCI),所以用OO思维的人,也不同意你的观点吧。单独思考的话,没所谓。

还有原文中提到的数据结构+算法与设计模式交替学习,这种学习方式,真不建议新手学习(你就算了,你貌似不算新手),这是一直以来学OO最害怕,害怕啥?害怕成为没跳出来的OO,而这样的事例大有人在。也就是banq在其它帖子所说的“中国病态教育”,也造就了中国OO软件行业发展缓慢。嘛,反正无论如何学也得看悟性,只不过好的学习方法可以提高领悟几率。

(可能我话语带有针对性,所以读完后请呼一口气,“交流而已”,LZ的原文中有很多不错的比喻和理解的o(∩_∩)o )

2010年10月26日 13:55 "jdon007"的内容
banq过分强调其对立性,似乎banq曾说“要是谁再在jdon上说程序=数据结构+算法,就再骂他一次”,希望这次banq能“刀下留人”呀 ...

哈哈,过虑了,象你这么有深度的讨论让每个人都有收益,深入必然就带来细分,细分就带来封装,封装实际就是边界划分,封装也就带来对立。哈哈,语言只是带了人类主观色彩的工具,不必拘泥了啊。

Niklaus Wirth作为图灵将获得者,是偏算法数学这个领域角度的杰出专家,但是做得了专家,做不了“全”家,打个比喻:你向南走,走到南边的极端,在向南这个方向你是专家,你是No.1;但是我虽然是一个普通老百姓,我却走在向北的起点上,那么在宏观战略方向上,我们之间的水准其实是差不多的(呵呵有点狂)。

通过这个比喻只是说明一个问题:西方文明更多是一种数分细化文明,就像西医一样,你是儿科大夫就无法做外科,因为细分带来封装,封装带来对立,对立带来隔阂,隔行如隔山;而中国的象文明或者类似中医文明,是站在一个更高高度来看。

我们来看待这两个公式,我个人认为也应该从我们自己国人传统优秀思维的高度来看。

不知我是否解释明白,所以,千万别误会,我没有否定或者怀疑楼主的观点,只是做了封装比较,给人的感觉就象对立,因为这是当前思维方式主要方式:量化 细分。

另外,我非常赞同你的大部分观点。至少是一种创新,努力尝试。
[该贴被banq于2010-10-26 15:08修改过]

banq“发恶”那句话的确说过╮(╯▽╰)╭。

OO和PO又关联着又对立着,只认知一面,很容易会产生片面。但我们不可能两头兼顾,因为两端都是没底的,两头顾可能两头都不精,甚至会产生误解。所以说交流是很重要,自己主攻一面,然后在别人那里得到另外一部分认识(当然不可能会多的),全面认识而不失立场才是正确做法。

OO与PO是两套世界观,所以换掉概念是不正确的做法,也就这样而已,无他。

话说面向对象有什么奖?o(︶︿︶)o

2010年10月25日 18:40 "jdon007"的内容
主要的差异是,对象将变量与过程捆绑在一块了,致使调用过程与发送消息的形式不同,但这并非本质性的差异,它们都属于“命令式编程范式”,真正与它们“水火不容”的是“声明式编程范式”。当然,“命令式”与“声明式”虽水火不容,还是存在共性的,那就是程 ...

对于这个话题,我还是有兴趣再深入讨论,但是一旦深入就显露出我的屁股位置,因为屁股决定脑袋吧,当然你也知道我屁股坐在哪边立场,哈哈。

从命令式编程范式这个角度来看:数据+算法好像是和对象+消息差不多,但是如果从其他角度来看有问题的话,那么这个命题真理的边界大概就比较小,就只限于从命令式编程范式这个角度了。

我所谓其他角度,就是从名可名,非常名这个道理出发,名字很重要,高于一切,名字是一种共识,模式为什么成为模式?因为它也成为一种共识。

数据定义的共识是什么?算法定义的共识是什么,我想大家心里比较清楚,如果Niklaus Wirth把对象概念,硬是塞入数据来表达,也是可以的,因为很早时希腊哲学家都是数学家,他们都认为整个世界都可以用数来表达,这就是数文明;

但是别忘记,我们是中国人,中国古老哲学不是和数学家结合的,而是象学家,比如中医据形辩症,中国古人早就强调世界可以用象来表达,这是中国象文明。

我插入中国象文明的意思,就是说明:虽然哲学可以用数学表达,但是哲学不是唯一可以用数学表达,也不表示数学可以替代哲学。

如果肯定我这个概念,那么我们就不能用“数据”这个传统共识的数学用于来替代哲学。

在对象的象世界中,比如DDD领域驱动设计,用凝聚性来寻找辨识数据和算法,因为数据和算法之所以成为有意义的数据和算法,关键是它有意义,为什么有意义,因为它有内聚性,它由很多表达一定意义的内聚分子组成。

打个比喻,风扇不转是,叶片是分离的,这时我们定位为三个叶片,每个叶片是分离的,分别有自己的内聚原子或分子;但风扇转动,叶片以光速转动时,我们看到的不是三个叶片,而是整块铁,这就变成另外一种凝聚性特征。

所以,对象世界是从外部,象形来看待事物,分类各种数据和算法的。我想这种角度来认识世界是大众的,是哲学化的。

而单单从数据+算法来看待世界,将这个世界纳入自己的数据和算法内部,我个人感觉是牵强的,有儿子跳到老子头上的混乱(数学凌驾于哲学)。

所以,从程序技术实现角度来看,这两个公式好像都属于你说的命令范式,是差不多的,我认为这是战术层面它们可能是一样的,但是战略层面,也就是反映需求,以及战术和战略传导方面,对象方式就有其天然的一贯性了;而且声明式范式其实就是将战略和战术进来分离,将程序技术实现封装,从这个意义上看,我也认为这是对象化思路推动的结果,也是对象思维发展的必然结果。所以,你可以在DDD中看到声明式是其一个主要推荐。


[该贴被banq于2010-10-26 15:50修改过]

banq好哲学,我受教了。

我还真未深入到命名大小的问题上。我认识到对立统一的世界观而已。

banq说出实现上OO是数据结构和算法的再一次升华(分类、提炼),犹如这杯子是铁做,但铁不一定都是杯子。还有就是类似我第一个回复所说到的单元,单个可以由许多单个组成,但是在一定的世界观下,所有单个必有元存在(例如化学上,化学变化中最小粒子是原子)。而对于OO也一样,类好比banq例子中的大铁饼,而其中的数据结构和算法就是其叶子。(数据结构和算法有点类似材料)

banq这些观点都是xOy轴上的y轴观点。

而我观点集中与OO与PO是同一层,但又不相同的世界观上(对立,x轴的观点)。也正因为y轴的原因,所以他们是相似的。

综合起来,可能更好理解OO与PO的对立统一。
[该贴被SpeedVan于2010-10-26 16:15修改过]

使用“数据结构”、“算法”、“设计模式”这些术语,除了对前人的敬意,另一个原因就是不得已而为之,因为这些“名”已深入程序员的心,尽管每个程序员对其的认知可能不同,但再“名”也同样可能会出现“名可名,非常名”的无奈。康德也说:用人类理性发明的词语只能讨论现象,不能谈论世界的本质。

其实也不喜欢“命令式”和“声明式”的说法,只是前人已经这么说了,我也不好另起炉灶。不过我还是认为“面向过程与面向对象”,只是“方法论”层次上不同,而不是在“世界观”层次上不一样。“命令式”是以“事物”为中心,通过“运动”去改变“事物”的状态;“声明式”是以“运动”为中心,通过“事物”去改变“运动”的轨迹。个人以为,“事物”与“运动”的关系,可作为“世界观”的分类标准,而对如何去表达“事物”与“运动”,则作为“方法论”的分类标准。

如果术语碍手碍脚,就搁在一边吧,用“事物”、“活动”、“规律”这些生活化的词语来交流与思考吧,像个无知的外行好奇、困惑地观摩、思量着周边的世界,也许我们会感觉更自由,我的本意就是这点东西。如果放开束缚的话,程序也可以理解为《易经》中的“象”、“数”、“理”,“象”描述事物,“数”描述运动,“理”描述规律。

四五年前,我开始把数学当作一种语言,还有音乐、绘画等的等也是如此。“语言”只是“思想”的载体,其形式可百家齐放,但它们所承载的“思想”应该具有“一致性”。如果说“数学”是一种语言,哲学代表“思想”,这点与banq的认识是一致的。

“万物皆数”的观点大概认为“数”为“神”,“物”为“形”,这样也解释得通。将“数”理解为“物”的一种表达,那么也可以将“象”作为“物”的一种表达,也解释得通。至于哪一种观点更接近于世界的真相,就不得而知了,尽管我和你一样倾向于后者。