2005年2月28日星期一

Hike of Rattlesnake Mountain

今天和几个中国同事去爬Rattlesnkae Mountain。似乎这是我去年冬天以来的第一次hiking。

Seatllte附近多山,因此也有颇多的登山爱好者。连带着,也就有了很多供hiking用的trail。随便一个小山丘或者湖边,都有一两条trail。

在国内,北京附近爬山,常常是要么没有路,要么路边全是卖东西的。

而在这里,风格却很不同。到处有专门供人远足用的小路,但是没有什么人卖东西。似乎也没有什么人来维护。小路两边没有垃圾桶,也不会有人把垃圾扔在路边。感觉这里的大部分地方都已经被开发过了,但是又保持着一种很自然的面貌。

对于爱好爬山的人来说,这里确实是一个非常惬意的所在。

Rattlesnkae Mountain应该算是比较有名的爬山去处。今天爬上的一路上,见到了不少人。(相对于这里别的地方而言,和北京香山的人海是完全不能比的了)。很多人都带着狗。也有几个人背着小孩。

这次hiking的路程很短,只有2 miles,合3.2公里。不过等上山峰以后,视野到也是非常开阔。

最遗憾的是,我虽然带了DC上山,但是居然忘了带存储卡。所以今天这里附带的照片都是我的朋友Jun拍的。Jun的照片拍得非常漂亮,大家可以去他的blog看看。



照片链接:Rattlesnake Mountain

2005年2月27日星期日

出游(玩具商店,华盛顿大学)

春暖花开的周末,照例出去兜风。


中午先去了Bellevue Square,看了看朋友推荐的几个特色商店,Sharper Image和Discovery Chanel Shop。这几个商店都是卖一些特别的“玩具”,例如把时间显示投影到墙上的钟,电动的DVD架,在地毯上自动游弋的吸尘器。Discovery卖的东西相对要低龄化一些,比如用来侍养蚂蚁的模拟生态环境,能显示当前天空星座的灯。
与其说是卖“玩具”,不如说是卖idea的商店。很多东西都非常对我的口味。可是论到价格,于国人而言,却已经不能当作玩具来看待了。


这些天不少地方的樱花都开了。所以我们下午去了华盛顿大学,希望能看到盛开的樱花。不过可惜的,华大里的大部分樱花树还只是含苞待放的状态,算是有点遗憾了。



对于华大,我没有什么好说的。梅贻琦先生说,大学者,乃有大师之谓也,非大楼之谓也。
我只是看了看华大的大楼,当然是什么也说不出来了。

2005年2月26日星期六

《ASD》设计模式:Factory - 兼谈模式的滥用

operator new是个很特别的命令,只要一出现,就会破坏DIP(依赖倒置原则)和OCP(开放封闭原则),然而她又是不能避免的命令。
解决办法就是使用Factory模式,把new命令与使用者隔离开来。
然而依然需要一个地方来创建Facotry类的具体实例,不过这个工作可以放在应用程序的初始化部分来作,与其它应用还是隔离开来的。
关于Factory应该不用多写什么了。对于一个面向对象程序员来说,这可能是最常见的模式了。
不过可以谈谈什么时候该用Factory模式。

原则上说,当创建一个可能会变化的具体对象时,就应该(必须)要使用Factory模式。
然而,什么对象是会变化呢?
传统的设计方法是在最初的设计时就考虑这个问题。
可是极限编程的思想略有不同。

极限编程则认为,第一,在项目之初是很难预料哪些类是会变化的,所以不应该预先考虑这些变化。
说实话,对于设计工程师来说,要接受这个观点还真的有点痛苦。虽然我知道她说的是真的。
如果,软件不需要变化,那么面向对象的思想就失去了大半的意义。因此工程师们一定要让自己的作品能够灵活地响应变化,这也就是OCP的原则。
然而,当运用了大堆的strategy、template、factory等等模式,建造出一个叹为观止的系统后,只等着需求改变,这个系统就能体现出OCP的好处。这时就象是搭了一个完美的陷阱,只等着猎物经过。
可惜的是,猎物却常常并不从设置陷阱的地方出现。需求的改变总是出乎设计者的预想。那个陷阱成了一个完美的装饰品。
我写过很多这样的装饰品。他们唯一的用处就是给后来者作为学习的例子。

那么该在何时使用这些模式来让程序变得坚固而灵活可以承受需求的改变呢?
极限编程认为,第二,发生过变化的地方趋向于再次变化。套用一句老话,“可以跌倒,但是不可以在同一个地方跌倒”
就软件设计而言,在最初,并不去考虑应该应对什么样的变化。只有当第一次变化出现的时候,才去更改设计,让程序对这种变化能够遵守OCP的原则。并且,这种改动后的设计,应该能够应对今后出现的所有同类变化。
我想,对于设计工程师,这种观念应该是可以接受的。

设计模式的初学者,就好像拾到了一本秘籍的孩子。艺成下山,不管遇到什么都想施展一下拳脚。这就是模式的滥用。
如何避免模式的滥用,应该多考虑一下上面所说的两条原则。

2005年2月24日星期四

《ASD》包的设计原则-兼谈极限编程

我在之前谈ASD的时候,都有意回避了开发流程的问题。然而实际上敏捷开发就是极限编程的另一个名字。
对于一个了解传统软件工程的人来说,甚至对于一个有传统项目开发经验的人来说,极限编程的观念都有点难于接受。正因为如此,我闭口不谈极限编程,只谈论设计模式。无论如何设计模式是好东西。
到了“包”的部分,极限编程的话题再也绕不开了。
极限编程中最惊世骇俗的部分大概是,反对建模,直接实现功能,然后在软件具备一定规模之后,才由下向上的设计“包”。
如果过去有人向我提出这样的观念,一定会被我B4的,但是我现在到觉得,极限编程有一定道理。至少她在规模较小的项目中是有道理的。
该如何衡量项目大小呢?这又是一个很难的问题。但是可以确定的是,大部分的项目都是小项目。

极限编程认为,在实现功能之前,先建建立全局的模型是没有意义的,这样的模型只会限制软件的开发,然后模型和实现双方互相妥协,结果模型被改得面目全非,还是离理想结果相距甚远。

反对极限编程的人认为,没有总体模型的指导,实现会缺乏大局观,变得混乱重复,难于维护。

极限编程提高维护性的主要作法就是依赖“包”。“包”并不是传统的模块的概念,而只是一个软件结构的映射,用于管理依赖和重用。
而由于测试驱动开发,软件结构是非常容易变动的,所以可以在混乱刚出现的时候,调整“包”,来恢复整个软件结构的有序。
但是如果没有总体的模型,开发人员在实现功能的时候,如何才能知道有没有可以重用的包呢?在调整软件的时候,又如何知道该调整到哪个包里去呢?
这就是说,需要有人(很多人,最好是全部开发人员)了解整个系统的情况,知道包的结构。
这也是极限编程强调结对开发(pair development)的原因之一。结对开发的要义不仅是两个人一块干活,而且还是要经常变更自己的开发伴侣,开发系统中的不同模块。这样让知识在团队中广泛传播。团队中的每个人,都对系统中的各个部分都有所认识,然后软件的灵活调整才能得以实现。

我基本上同意上诉观点。还有两个推论:
第一个推论,项目的大小取决于开发人员的能力。由上面的讨论可以看出,要实施极限编程,需要每个开发人员都了解系统的结构。但是,事实上,个人的能力有限,在有限的时间中,能够理解的系统结构也是有限的。超出了这个限制之后,极限编程大概就会暴露出越来越多的问题。所以,极限编程确实在较小的项目中比较有效,这个大小的限制即是开发人员能够理解的系统的大小。团队中开发人员平均水平越强,极限编程能够支持的系统也就越大。
推论的推论,如果是作一个个人作品,毫无疑问的应该使用极限编程。
顺便一提的是,与极限编程对应的另一种方法是软件工厂,即是由少量的精英的制定模型,大量的软件蓝领实现极细小的模块。虽然我由衷的不喜欢这个点子,但是也许这种方法更适合中国的国庆。
第二个推论,极限编程的各个部分有着密切的关系,分开来应用其中的一条两条,并非极限编程。

好了,下面来谈论包的设计原则。前面已经提到,包的主要目的是管理软件模块的依赖关系和重用。包的设计原则就是为了使这样的管理尽可能的最优化。
下面来看看这些原则。这些原则的前提是,这是一个应用极限编程的项目。即是说,项目的规模有限,并且已经有了一定规模的类,现在要用包来管理这些类。

内聚性原则:包括REP,CRP,SRP。这里主要解决的是包的粒度问题,多少类该被放进同一个包中。然而,包其实并不等于软件模块,所以包的大小在通常情况下,是远小于软件模块的,就是说,同一个模块可能被放在多个包中。
REP是重用发布等价原则,重用的粒度就是发布的粒度。即是说,一个包中的软件要么都是可重用的,要么都是不可重用的。我的理解是,实现feature时要想想,那些部分是这个feature独有的,那些部分是其它feature也可以用的。这两个部分要分入不同的包中。
CRP,共同重用原则,一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类。我的理解是,即使一个feature中有很多部分都是可以重用的。但如果这些部分可以单独重用,那么也要分入不同的包。
CCP,共同封闭原则,包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其它的包不造成任何影响。我的理解是,应该把对于同一个变化敏感的类尽量放入同一个包中。
用不太准确的说法来说,前两个原则的动力来自包的client,趋向于拆分包;后一个原则的动力来自包的server,趋向于聚合包。包的粒度就是在这两种力量之间的动态平衡。需要注意的是,所谓包的client和server可能和你的想像有所不同。

耦合性原则,包括无环依赖原则,稳定依赖原则,稳定抽象原则。这些原则主要用于解决包之间的依赖关系。
无环依赖原则,在包的依赖关系图中,不允许存在环。所谓包的依赖是指,如果A包中的文件依赖B包中的文件时,就是A包依赖B包。(之前在DIP原则(类的设计原则)中,已经提到了实现应该依赖于接口。)这是一个应该严格遵守的原则,只有遵守了这个原则,才能谈后两个耦合性原则。依赖关系中如果出现了环,就要解决掉环。解决环的方法有两个,第一个方法的提示是DIP原则。第二个方法是,把两个包都依赖的部分提取出来作成第三个包,让两个包都依赖于这个包。这个方法的提示是,包在开发过程中是动态变化的。
稳定依赖原则,朝着稳定的方向进行依赖。所谓稳定性,即是包的依赖关系。若是包A依赖一大堆其它的包,那么任何一个包的变化,都会引起包A的变化,包A即是不稳定的。相反,若一大堆的包依赖包B,那么包B若要变化就会引起很多其它包的变化,这么说包B很难改变,包B即是稳定的。《ASD》中描述了一个计算不稳定性的方法,称为不稳定性度量I,主要取决于依赖于包的类的数量和包中依赖于包外的类的数量,具体算法很简单,略。I是一个比值,范围是[0,1]。0时最稳定,1时最不稳定。这个I值对于类的划分有点意义,要把不变的类放入I值较小的包,把要变的类放入I值较大的包。但是I的主要意义是表现在稳定抽象原则中。
稳定抽象原则,包的抽象程度应该和其稳定程度一致。理论上,抽象类即是接口,趋向于不变,具体类即是实现,趋向于变化。《ASD》再次给出一个描述包的抽象性度量A,即是用包中抽象类的总数除以保重类的总数得到的比值。这个抽象性A的取值范围是从0到1,数值越大,抽象性越大。因此,在理想的系统中,I越大,A就应该越小;A越大,I就应该越小。若是把A和I作成二维的坐标体系,那么(0,1)到(1,0)的连线就是一根理想连线,若是一个包远离这个连线,那么这个包的设计就是有问题的。
这个让我想到,也许应该设计一个软件来自动管理包的A值和I值,自动给出统计数据。那么对于系统的设计应该会有很大的好处。也许,过一段时间,等我手上的项目不太多的时候,我也许可以写一个这样的东西出来。

一个问题,在C++中,如何表现“包”呢?不明白,要想办法查查看。

Subscribe with Bloglines



2005年2月22日星期二

周末郊游

兜风一整天,拍了几张照片。

照片链接:Outing

2005年2月19日星期六

Everything About Everything

前天我工作所用的Windows忽然不能启动,重装系统也告失败,所以就只好向IT部门求助了。
IT部门的兄弟说,尽快给我送个哥们来帮忙。
于是我就一直待在办公室里等待。可是,这两天,恰好,IT兄弟们赶上了农忙季节。竟然把我凉拌了一天也没有人来理我。
于是,我只好在办公室里看文档。把之前想看而没有时间看的文档全读完了。
之后开始读书,一口气读完了五章《ASD》,连读书笔记都写了三篇。

晚上回家的时候,在路上听iPod,听到了《the Big Blue》配乐二。一开始穿插了很多电影中的台词。
Enzo声音低沉,磁力无限地说:“welcome to blue”。

这几天来,每日的天空都干净得耀眼,晚上星空灿烂。
我已经熟悉了北斗星和猎户星座。昨夜,在回家的时候,又凭着中学时代的记忆,顺着北斗七星,在北方天空中找出了北极星。
发现每日上班走的148路,几乎是完完全全的正南正北指向。

iPod中,传出了Enzo和Jacque的对话。
What do you want to know?
Everything.
Everything about what?
Everything about everything.

〖转贴〗中国人的种族观念

【若昔难得 按:】写这文章的人是个爱尔兰老外。于国人而言,对于他的指针,可能有很多都会断然否决,或曰以偏概全。
也许是以偏概全,也有可能是旁观者清。
中国正在迅速地走向世界。之前,中国较少接触外面的世界,所以种族观念并未彰显。
而现在,与外界的联系日益加强,中国之国力也与日渐进,种族问题确实是值得国人注意。

就该文所讲的具体实例,在我自己的身上,也有那么一点痕迹。
比如,一般情况下,我管外国人为老外,但是,对于黑人兄弟,我却常常会称之为黑人。
我可以用这个理由来辩解,在我们常见到的老外中,黑人兄弟比较少,所以称黑人,有更准确的指向性。而且,我确定,自己对于黑人没有任何偏见。
但是,如今仔细想来,黑人兄弟发现自己在中国,竟然连“老外”的称呼也不能享用,应该是有点不爽的。
所以,我今后,一定会注意对于非白人老外的称呼问题。
称呼事小,然种族观念问题事大。而且这种观念,就是在这么一点一滴中慢慢积累起来的。

原文链接:中国人的种族观念

〖转贴〗1905:三种力量角力中国

【若昔难得 按:】一百年以前,“山雨欲来”。保皇派、立宪派、革命派,只是这百年风云的开始。之后的风起云涌,到有点不知如何说起的意味。
转念间就想到一句话,百年如一梦。
不过立刻就明白,所谓“梦”的说法,是不折不扣的bullshit。
凡走过必留下痕迹。
百年之后,再回头来看,2005又留下了什么痕迹。

原文链接:1905:三种力量角力中国

〖转贴〗让我们接近星星

【若昔难得 按:】先哲康德有句名言,头顶的星空与心底的良知。这话听起来很cool,有种让人感动的意思。
然而,我以外,越是让人感动的口号,我们越要加以提防。仔细分析,他究竟是在说什么,有何依据。理性是人类的最强武器,一定要善加利用。以上所说的,并非针对康德的这句名言。
在下对哲学家们先是敬而远之,后来又是敬谢不敏,所以对哲学家们实在是有点距离感。
单就康德而言,我倒是零星地读过他的一些言论。他的理性和反宗教的一面,很为我欣赏。大概算是哲学家中,最对我胃口的那一支了。
什么是头顶的星空呢?康德似乎是指无法穷究的自然运行规律。
是的,在人类有限的时间中,我们是无法穷尽这世界背后的规律的。然而,我们可以知道的,可以作到的,却远比大多数人心目中的那个界限更大。
实际上,这个世界上没有神,(相信神的诸位请原谅在下身为骄傲的无神论者的狂妄)更没有神定下的界限。
科学是没有边界的。

原文链接:让我们接近星星

《ASD》设计模式:NULL Object

CFoo * pcf = bar.GetFoo();
if ( !pcf && cf.Ready() )
{
pcf->Execute();
}
这是一段非常常见的代码。但是有人觉得!cf的写法不够优雅,就说可以用NULL Object模式来简化这种写法。

NULL Object模式大致说来,就是从当前的CFoo类中,派生一个CNullFoo类出来,这个类什么事情也不做。比如说他的Execute函数可能就是一个空函数。
然后,当bar.GetFoo()失败了,原本要返回一个NULL的时候,就返回这个CNullFoo类的实例。
于是,在调用端,就不用做!pcf的判断了,不管GetFoo结果如何,都可以一致的对待。

这个方法虽然看起来不错,但是好像没有省多少事,却还增加了一个类,是否有点不值呢?

对于像我这样的C++程序员来说,可能不会马上想起nested class,但是《ASD》一书,是使用Java来写例子程序的。
所以,这个CNullFoo类,其实是CFoo的一个Nested Class。这样,这个方案就可以接受了。
但是在C++中,我还是觉得这个模式是没有什么价值的。

顺道一提,我对Java的看法。
我热爱C++,所以对于Java多少会有点欠缺热情。不过我希望我下面说的话尽量公正。
一般说来,C++有两大缺点,一个是和C语言兼容,一个是复杂性。
就和C语言兼容而言,确实不爽。然而,这更多是编程习惯的问题。大家都知道,那一部分是C++中不太光彩的部分,那么不要去用它不就好了吗。(我要故作天真状了。)
而对于复杂性,我个人觉得,还可以接受。相反倒是Java不够复杂,就丧失了很多非常cool的特性,而变得不够灵活。在复杂和受限之间,我宁愿选择复杂。
但是Java有两个特性,使我非常艳羡的。
一个是语言的动态性(反射机制),另一个是nested class。都是目前的C++在语言一级上,很难实现,又非常有用的东西。不过,好像在C++/CLI中,这两个特性都会出现。
^_^ 这太让人期待了。

《ASD》设计模式:Singleton和Monostate

Singleton几乎是最简单的模式了,我甚至认为,在一般情况下,他不是模式,而只是一个idiom。
但是,他确实功能强大,而且非常实用,又很常用。(所谓居家旅行杀人灭口必备之工具)
关于Singleton没有多少可以说的了。一句话的解释是,利用静态指针或引用来维护一个系统中某个类的唯一实例;利用静态方法来获取这个唯一实例;是全局变量的优雅替代。
《ASD》一书中,也没有对Singleton讲出多少新意来。

对于利用静态变量做出来的技巧,都可以考虑用模版类来自动化。Singleton就是一个非常好的例子。这个问题我会在以后关于《Modern C++ Design》的记录中来讨论。

Monostate模式却有些新意思,甚至有点搞笑的味道。
如果,以各类可以有多个实例,但是没个实例中的成员都一样,那又如何?那就是说,不论这个类有多少个实例,其实每个实例使用的内存都是同一段静态内存。每个实例都是同一个实例的不同化身。这也算是全局变量的优雅替代。
但是Monostate的实现要比Singleton来的麻烦,必须把每个变量都声明成静态变量。
那么这样做有什么好处呢?
《ASD》如是说:
透明性:使用者使用Monostate对象和使用普通对象没有什么区别。
可派生型:每个Monostate对象的派生类依然是Monostate。(我的理解是,这个特性的前提条件是,派生类不能添加非静态成员变量)
多态性:派生类中可以override父类的方法。
我认为透明性其实没有什么意思,而且有点危险。如果调用者不知道自己调用的是Monostate,而把它当作普通类来使用,那么Monostate的表现可够她大吃一惊的。
可是后两个特性却很有意思,非常有意思。
仔细考量Monostate的内存布局。所有的monostate派生类都共享同一块内存,但是派生类的函数内容却有所不同。也就是说,如果把所有的monostate类对象看作是同一个对象(从内存的角度来看,他们也的确就是一个对象),那么这个对象的函数会根据调用点的不同,而由不同的表现。
即,这个对象的功能可以响应当前的状态。这是一个非常优雅的状态机
这就是Monostate模式的真面目,全局唯一的状态机。
如何实现Monostate模式的状态机,关键一点就是,在父类中,包含一个到子类的指针。父类的所有函数,都委托给该指针指向的子类对象。子类的函数,在状态切换时,把这个指针改变为指向下一个状态子类的实例。

《ASD》设计模式:Facade和Mediator

Facade模式和Mediator模式有些相似,虽然尚未相似到令人混淆的地步。在形式上他们是有明显区别的,在功效上却有共通之处,这样的相似更应该注意。
按照GOF的《Design Patterns》一书,Facade模式和Mediator模式都是用于连接多个类,简化类之间的联系。
Facade模式是把大系统藏在自己后面,由自己来做一个通用的接口。使用者不用关系下面的具体类,只要了解Facade的接口,就可以使用系统了。这几乎是最常用的设计模式了。
Mediator也是连接大系统,但是它的客户并不是系统的使用者,而是系统中的各个模块;相应的,它的目的只是为了降低系统中各个模块之间的依赖关系。而对于系统的使用者,Mediator常常是透明的。

《ASD》一书中,不仅强调了这两个模式的更多特性,还挖掘了这两个模式之间更内在的关系。即他们都是为了施加策略,Facade模式是从上面施加,Mediator是从下面施加。
所谓策略,即如何使用系统中的模块。
于此一说,如何应用Facade模式来施加策略就昭然若揭了。既然调用者只是发出命令,由Facade对象解释命并调用系统中的模块来执行命令,那么Facade对象就可以在执行时,控制各个模块之间的关系了。
这个有些Template模式或Strategy模式的味道。
由于是从调用这一端执行,所以可以说,这是从上面施加策略,或者可以说是一种主动策略

在《Design Pattern》书中,Mediator对于系统的客户是透明的,但是系统中的各个模块基本上都还是指能够看到Mediator。
可是在《ASD》书中,Mediator对于系统中的各个模块基本上也是半透明的了。
Mediator知道所有的模块,和他们之间的关系。
而各个模块并不知道Mediator的存在,它们能看到的,只是一个消息系统,有事就发消息。
当然了,Mediator就在消息系统的另一端倾听。一旦收到了某个模块的消息,他就开始调用相关的其他对象,牵动整个系统。
策略就是如此在系统内部运转,这是这次除了Mediator自己以外,没有对象知道,策略已经开始运作了。
所以可以说,这是从下面施加策略,或者可以说是一种被动策略

两者相比较,主动策略来的更直接,力量更强大。但是如果调用着绕过Facade的接口,直接与系统中子模块对话,策略就会无法实施。
被动策略是隐蔽的,是以相应的方式来运作的,在策略的实施上,有些限制。但是没有模块知道策略的实施,也就不能绕过这些策略。

2005年2月17日星期四

《ASD》设计模式:Template和Strategy

Q:virtual函数可以是private函数吗?
A:可以。不仅可以,这还是Template模式中的常见方法。
Q:那么子类实现这些不能被调用的虚函数有什么意义呢?
A:意义在于这些虚函数是由一个public的非虚函数来调用的。
Template模式的核心是有一个相对固定的调用框架和可以被替换的具体步骤,父类实现这个调用框架,子类实现具体步骤。如果不想让客户随便的使用这些具体步骤,就可以把它们设为private函数,只能由父类的框架来调用。所以,对于一个类似于framework的产品,Template模式几乎是必不可少的。

Template模式是一种非常强大而且有趣的模式,但是它常常会被滥用。根本原因是:仔细考量Template模式的结构,会发现Template模式的实现完全是依靠继承。而GOF说过,要优先考虑聚合然后才是继承。
Template是直接继承,这使得具体实现的子类依赖于调用者父类,这不符合DIP原则(还能想起什么是DIP吗?)这就使得子类中的方法不能为其他对象所用。而常常,对于一个具体实现有不止一种策略,而每种策略都需要这个具体实现中的某个方法。这种时候,Template模式会变得很麻烦。
那么如何才能让它符合DIP原则呢?这就需要在调用者和实现者之间添加一个接口。这个接口声明所有的可能被调用的步骤;调用者包含这个接口,调用接口中的方法;实现者从这个接口中派生,实现接口中的所有步骤。
这个改动将大大地改善之前设计的灵活性。多种策略可以共享同一个实现了,当然一个策略也可以应用于多种实现上。策略和实现之前完全的松绑了。
实际上,这种设计是把之前的Templage模式便成了Strategy模式。
所以当,涉及到要把上层逻辑和下层实现分离的时候,应当优先考虑Strategy模式,而不是Template模式。

那么Template模式是否就没有用了呢?看到这种问句的人都该明白,答案一定是否定的。
刚才已经提到了,Template模式的问题之一是实现者的方法不能被其他对象使用。
而本文一开始就说了,Template模式可以把虚函数设成私有函数,以防止其他对象滥用具体实现类。也就是说,这是一个故意加上去的限制。
现在明白了吧,Template的不灵活原本就是他的意图,本意就是要给使用者添加一些限制。例如一个framework的产品,并不希望出现自己所不愿意见到的调用方法(那样也许会对产品本身造成破坏),所以就会使用Template模式。

结论是:Template模式在于限制,Strategy模式在于分离。

2005年2月16日星期三

《ASD》设计模式:Command和Active Object

Command模式:
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. - 《Design Patterns》

Command模式的关键在于只包含一个 Execute方法,子类在实现这个接口时,在Execute方法中,完成特定的任务。可以说,这是一个非常简单的模式。
《ASD》中提到了该模式的三种用法:
1. Invoker可以和任意一个Command挂钩,而且不需要了解这到底是个什么ConcreteCommand,然后在需要的时候调用这个Command对象的Execute方法就行了。这在消息驱动的的系统中非常常见,每个trigger就是一个invoker。那么如何把Command和invoker挂钩呢?方法很多,最cool的方法是在系统外用一个配置文件来指定。这样不需要重新编译就可以改变软件运行的方式。可以参考Source Insight的界面。Source Insight中可以任意配置菜单项和工具栏按钮。其实现应该就是应用了这种Command模式。
2.上面的方法是否让人想起了Template模式?有点相像吧。顺着这个思路去想,就可以把Command模式应用于Transaction。让一个类来解决Transaction的init和uninit问题,中间包含一个Command的队列。这样就可以把这个队列中的全部command当作一个transaction了。这样的作法可以把Transaction的实现和逻辑分离开来,是很漂亮的实现。同样的思路,也可以用在类似的问题上,需要init和uninit,中间有不定量的操作。
3.如果真的用来解决transaction问题,那么就必须具备roll back的能力。然而这个很容易实现,只要在command类中,添加undo方法就可以了。剩下的活交给invoker来处理。
4.此外还有一个附带的好处。command类和一个单独的execute方法其实很相似,但是command类的对象有生命周期,可以由程序来控制。因此,一个command对象,可以在提交了很长时间以后再批量执行。
除了这些以外,《Design Pattern》还提到了Command模式的其他使用方法。虽然这些方法未必使用,但我还是把它们列在这里:
1.command对象和command对象的序列都可以serialization。这样如果软件被有意或无意的中止(例如crash),在重新启动后,还可以接续之前没有完成的任务。
2.Command模式如果和Composite模式接合,就可以作出MacroCommand。^_^,这个idea虽然很cool,但是可以用到的地方大概不多吧。

Active Object模式不属于《Design Pattern》23模式。实际上,她是一种特殊的Command Queue。其特殊之处在于:
1. 队列的拥有者会顺序地执行队列中所有Command对象的Execute方法。(这个其实不算特殊)
2.Command对象在自己的Execute方法结束前,可以把一个新的command对象(实际上常常是这个command对象自己)再加到队列的尾部。
看出来了吗,这个队列有可能不会终止的,他可以一直执行下去。这个可以作为一个应用或者服务的主模块了,想像一下她可以作多少事情吧。
《ASD》指出这个模式可以用来在一个线程中处理多任务的问题!!! ^_^ 太cool了。
如何处理呢?你可以把每个command对象看作是一个任务。他在Execute函数中,处理自己的任务,在任务告一段落时,记录自己的状态,然后把自己插入到队列的尾部,结束Execute方法。当队列轮完一周后,又会再次执行这个command对象的Execute方法。 ^_^ 很cool吧。
那么这种方法和多线程的方法相比有什么有缺点呢?
最大的优点是,所有的command都在同一个线程中,因此切换时,不需要进入内核模式!!超高效啊!!而且,可以有很多很多的command,数量上远远超过多线程的数量。
缺点嘛,是这种方法需要用户自己来实现调度,另外这其实是一种非剥夺模式的多任务,如果command处理不好,就会连累其它所有的command,因此实际上比多线程要更复杂。(嘿嘿,程序员能够怕麻烦吗?)
还有一点,Active Object运行于单线程,也就是说,她不能享受多处理器或多处理器核心带来的性能上的改善。
其实,这最后一点是非常致命的一点。也就是说,在当前intel的超线程CPU机器上,如果系统的负担并不重的时候。Active Object的效率有可能比多线程更低。
Anyway,这是一个非常有趣的模式。只是一般的程序员可能没有机会用到。但是请记住她,也许能有那么一次机会,可一用她来爽上一把。

异形中的两只狗

去年年底的时候,复习了一遍异形四部曲,看到一个很有趣的细节。在第一集中,Ripley养有一只小狗。很乖巧的小猎犬,就是Snoopy那种。
电影的后半段异形在飞船中,横冲直撞,大肆屠杀。可这只狗却安然无恙,虽然她也曾经被异形追踪过。在异形四部曲中,能够从异形口中逃生的,除了女主角Ripley以外,就是这只狗了。最后,飞船上的船员,除了Ripley之外,全都被杀死。只有Ripley和小狗乘坐救生船逃生了。
是小狗的求生能力很强吗?当然不是,只是导演很强而已。在好莱坞的电影中,坏人可以死,好人可以死,连世界毁灭也不算什么,可是杀死一只无辜的小狗,那可就真是大事件了。
所以,第二集中,Ripley一醒来,首先就要去找那只小狗,小狗没事就好。至于Ripley在太空中漂流了70多年,连女儿都已经死了。观众于此,虽然有些感同身受的伤感,却也还能够接受。
之后,Ripley第二次出发去和Aliens干架。这次如果再让异形们把人类杀个干净,偏偏留下小狗作活口,剧情上就太不通顺。可是又万万不能杀小狗,怎么办呢?导演这次把小狗留在家中了。让女英雄Ripley一个人去拼命吧。
到了异形第三集的时候,导演换成了新锐大卫芬奇(David Fincher)。这哥们尽管现在很牛,可在异形第三集的时候,即没有拍Se7en,也没有拍Fight Club,仍然是个毛头小伙子。所以他一心要把异形第三集拍成一个反传统的电影。其决心之大,在于电影故事还没有开始,就把第二集中和Ripley有点感情戏的大兵和Ripley拼死救出的小女孩给杀了。我敢肯定,如果第二集中,Ripley把那只小狗带在身边,那么那只小狗也必定会被大卫给杀掉。
可是没有杀狗,多少让大卫有点不过瘾。所以大卫安排异形首先寄生在监狱里的一只狗身上,然后破狗而出,再大肆咬人。对观众来说,杀狗可比杀人要震撼多了。所以,荧幕上,异形可以把人撕碎,但是绝不能正面出现异形杀狗的场面。
后来,异形第三集的DVD中,有一个加长版,并不是由大卫剪辑的。在这个版本中,异形寄生的是一只牛,而不是狗。没有杀狗的情节了。
我看DVD的时候,首先看的是加长版。看到牛被杀死的时候,确定这个加长版是个垃圾,然后就切换回原版去看了。

提到电影中被杀死的动物,让我想起,在教父第一集中,有个很出名的情节。
教父为了威胁电影制片人,把制片人的马杀死了,再把马头藏在制片人的被子下。制片人醒来时,被吓得半死,然后就乖乖听话了。
科波拉在教父DVD的评论音轨中提到,电影上映后,有很多人打电话或写信来批判为了拍电影就杀死一匹美丽而无辜的马。
科波拉解释他们并没有杀马。他们本来打算用一个假马头来拍这段戏,可是效果不好。后来他们发现有宠物食品公司用马肉来作宠物食品罐头,于是他们就找到宠物食品公司,选了匹漂亮的马,买下了马头,用来拍了那段戏。当然,马的身体还是被用来作成了罐头喂给了猫和狗。

未成年天后

从网上下载了法国未成年天后Alizee的两只MTV,看了以后,的确有点惊艳的感觉。Alizee出生于1984年,15岁就创下了百万销售成绩。虽然非常年轻,脸上稚气未脱,但台风却稳健成熟,很有些巨星的味道。

不由得让人想起另一个年幼成名的法国巨星Sophie Marceau。Sophie为广大国人所知大概是从braveheart开始,但其实她是由她的电影处女作《初吻》而成名的。而那时她才刚刚14岁。

看看《初吻》时的Sophie和现在的Alizee,到有些相似的地方。都是掩盖不住的青春年少咄咄逼人,却又充满了成熟性感的味道。
不由得想起张爱玲年幼时说的那句话,“成名要乘早啊”。
以前我是小生,如今都可以自称老夫了,却也没有能够成名。确如张爱玲所说,“快乐也不那么痛快了”。
张爱玲在这篇喊出了“成名要趁早”的《天才梦》中,还有更棒的一句话,“生命是一袭华美的袍,爬满了蚤子。

相关链接:
Alizee的两支MV,都是720X480的高分辨率。下载时注意量力而行。
La Isla Bonita
I'M Fed Up

2005年2月15日星期二

《ASD》:Principles

Agile Software Development是一本很棒的书,主要是因为作者是Robert C.Martin。当年我在ObjectMentor的网站上,读Robert C.Martin的文章时,就经常有背后的寒毛都竖起来的感觉。他对模式的理解和应用,常常让已经把GOF那本书读了四五次的我感到自己对模式还没有入门。
这本Agile Software Development我只看了一半,而且有一个多月没有看了。
所以我打算复习一下,然后把她看完。
今天复习的内容是Principles。
在我看来,Principles是软件设计的基础。和patterns不同,Principles不能直接解决任何问题。但是她是衡量设计好坏的标准,定下了设计的目标。
在复习之前,先回忆一下,能够想起的Principles有这么几个:
SRP,Simple Responsiple Principle,单一责任。即是,每个类应该只承担一个责任。衡量的方法是,在系统中,一个类只会随一种变化而改变。然而具体实现中,这是非常难以作到的,更实际的目标是针对接口。每个接口只承担一个责任,而继承了多个接口的类,就承担了多个责任。
OCP,Open Close Principle。即是对功能的扩展Open,对代码的修改Close。在不修改当前代码的前提下,只是添加新的代码,就可以扩充功能。实际上,没有绝对的Open,也没有绝对的Close。无论如何抽象接口,分离实现。代码中,总有一点是需要修改而无法close的。但是随着动态语言特性的引入,我想,设计中对OCP的支持将会被大大的增强。
替换原则,想不起来英语简写了。(三个字母的黑话太多了 ^_^)字面的意思是父类的应该能被子类替换。实际上,是指子类不应该扩展父类没有的接口。更深层的原因是,如果子类公开了父类没有的接口,那么在使用时,调用者将会判断正在使用什么子类,是否可以使用扩展接口。这就使得调用者依赖于具体的实现,造成了高耦合。那么如果子类一定要扩充接口怎么办呢?答案是继承多接口。
依赖倒置原则,想不起来英语简写了。(三个字母的黑话太多了 ^_^)相对而言,这个原则比较容易理解。即是,模块之间不应该有直接的依赖关系,而应该通过抽象的接口联结起来,彼此不关心对方的具体实现。这个principle虽然容易理解,然而在实施上却是颇为困难。
剩下的principles就想不起来了。现在去看书,一会儿来更新。

复习以后的更新内容:
1. SRP,没有什么可以说的了。
2. OCP,没有什么可以说的了。
3. 替换原则的简写是LSP。子类不仅不应该扩充接口,而且也不能消减接口。总之不能让客户者针对实现作出判断。然而,我现在想来,这与GP中的traits的用法似乎有点不合。关于这个问题,我要仔细想想。结论大概是,traits的用法是,实现者根据调用者的不同,提供不同的实现,但是仍然保持着接口的不变;对于调用者而言,不同的实现还是透明的。
4. 依赖倒置原则是DIP。更容易的理解是,所有的依赖方向都应该是由实现到抽象,并终结于抽象。具体一点的说,上层模块要定义自己需要调用的接口,并使用这个接口,所以依赖这个抽象。下层模块要实现上层模块定义的接口,所以依赖那个抽象。两层模块的实现都依赖于同一个接口。
5. 没有想起来的原则是接口隔离原则(ISP)。实际上是说,要避免胖接口,尽量使用瘦接口。核心思想是,不能强迫客户包含他们并不需要的接口。也就是说,不要单一的大接口,要有很多的小接口。实现可以使用多继承,也可以用包含的方式来实现这多个接口。

其实,以前就觉得,Robert C.Martin和其他大师相比,非常倾向于使用小接口和多继承。如今看来,他有一大堆的Priniciples来支持自己的这种设计。

2005年2月13日星期日

Heartbreak Heartbeat

今天和两个同事去看电影。这几天,在Oscar快要到来的时候,虽然有不少很棒的电影上映;但是,考虑按照我们的英语听力,如果选择The Aviator, Million Dollar Baby或者Sideways,都会有些浪费的感觉。(虽然我很期待Sideways)
所以我们最后选择的是13警局。这电影与我们所设想的东西完全吻合,是完全没有什么可以说的了。

然而在电影之前,却偶然看到了一个很棒的广告。黑白的广告,穿插着巨大的New York中的琐碎生活,修鞋匠的手和锤,憔悴的华人的脸,白色的袜子和拥抱而舞的老人,房屋街道大楼,消防员警察和玩篮球的人。
广告的第一句台词,“My oldest friend My first love”让我很有感觉,那感觉一直持续到最后一句广告词,“My card is american express”,就被彻底的破坏了。
画面中有张晃动的苍老的脸,依稀是Robert Di Niro。这让我想起了那部伟大的电影《Once Upan a Time in America》。面条在“yesterday”的音乐中,回到他的故乡大苹果NY。那里曾经有他最好的朋友,有他的初恋,有他的青春梦。而现在,一切都变成了Yesterday。
城市是人群聚居的地方,对于每个人来说,城市却是一个私人的体验。她和人一起长大的,一起成熟,一起老去。那不仅是一个地理位置,更像是一个记忆的标签。
心脏怦然而动,周围的一切都淡去了,那一刻犹如永恒般的漫长;抑或,心脏如撕裂一般的痛苦,无法行动,只能停下来咬牙忍受。当思绪触到那标签的时候,这些记忆依然鲜活得犹如就在脑后。

回来以后,google了一下,才知道,那演员果然是Robert Di Niro,而导演是Maritn Scorsese。全部的台词如下:
My oldest friend My first love
My east My far east
My west side My private side
My heartbreak My heartbeat
My life happens here
My card is american express

无论画面和语言如何的精致,这还是一个商业广告。在翻动伤感的回忆之后,是赤裸裸的“My card is american express”,确实让人非常的不快。

从下面这个链接可以观看或下载这个短片:american express

2005年2月9日星期三

中国怪物 新春

时差16个小时以外的地方,现在已经是他们的新年了;本地的时间在中国人的说法中是大年三十,虽然本地人大多不知道这个称谓。
这让我有些困惑,犹如春节本身也让我有些不知该如何面对。
我不喜欢春节。传统的春节里,每个人都有固定的行为模式,拜会成堆的由各种亲疏远近关系连起来的数不清的人,说一些不解其意的吉祥话,傻乐之后回家睡觉。
人们在这些日子中都要以团体的形式出现,杂烩成一锅,个人自由欠奉。
关系人情人伦礼仪,中国人传统中维系整个社会关系恒久不变的那套法门,就在这数日中轮番上演。
所以我真挚地不喜欢这个节日。
我宁愿在这个假日中,把自己关在家中,看书看碟大笑打屁,间或吃饭睡觉,不分白天黑夜。
这是我最愿意的,在寒冷而漫长的七天假期中,所作的事情。
然而,七天的无日无夜以后,才会明白,原来我也未能正视那个被称作“年”的中国怪物。
那个我爱的,我恨的,我想离开的,却永远也无法放手的怪物。

16个小时以前,时差16个小时以外的地方,还是中国人称为大年三十的时候,我和几个同事,坐在Redmond的老四川饭店,点了一盘水饺。我们商量,那一天才是我们的年。最后决定和中国同步,按着北京的时间来吧。
当今天早上,我离开公寓去公司的时候,在中国,刚好是新一年的第一个小时。在中国,人们寒暄,孩子嬉戏。他们还有半个黑夜,才能见到新春的阳光。
而在Redmond,在我的路上,我看到天空是均匀的蔚蓝色,灌木枝条的末端有粉红的芽,走在草地上,脚下传来冰凌被踏碎所发出的“咔嚓咔嚓”的声响。

2005年2月7日星期一

〖转〗光的真理

〖若昔难得按:〗
在光的问题上,真理只有一个。
在很多时候,当人们说没有对错是非之分的时候,真理还是只有一个。
否认这一点的人,常常只是不愿或不敢或不能看到那个真相。

插一段题外话,这篇文章中提到了牛顿。对于大多国人来说,牛顿的名字是和苹果连在一起。我宁愿相信那个苹果的故事是牛顿的一个joke。
如果那个故事是真实的,那么在中国也有一个类似的故事。朱熹看到井里的白气,就悟到这个世界是由阴阳二气组成的。
为什么,两个相似的故事之后,一个人带来了伟大的经典物理,另一个人则满嘴胡话呢?
好象又回到了那个经典问题,答案依然是,推理和实证是科学的基础,缺少了这样的基础,就只能臆测和胡说了。

原文链接:光的真理

2005年2月4日星期五

〖转〗杨振宁:在科学与玄学之间

方舟子所写的这篇《杨振宁:在科学与玄学之间》虽然由杨振宁生平讲起,但其实话题还是落在了所谓的李约瑟难题上。
爱因斯坦给出的那个答案,“古代中国学者不懂得形式逻辑体系和实验验证,因此没能发展出近代科学”,在我看来,比杨振宁的答案更为确切。
我以为,实际上,形式逻辑体系和试验验证的缺失是《易经》产生的原因,而非《易经》的结果。
只是,在中华文明中,为何没有能够产生形式逻辑体系和试验验证,确实是一个问题。
爱因斯坦的言下之意,这是一个偶然的结果。
那么这个偶然也过于悲哀了。

原文链接:〖转〗杨振宁:在科学与玄学之间

故事和道理

黄章晋在《理直气壮地无耻——我读《狼图腾》》一文中,开篇就写到:

很多中国文人最爱的智力创造是这样一种东西,先编个故事,然后根据故事讲述一番很大的道理。如果你不明白,随便找本《读者》,我马上可以找出十篇来。通常,这十篇里有八篇讲的故事一望而知就是不可靠的,或是作者生编,或是作者智力不逮当了回谣言二传手。接下来的奇观是,这十篇里还有八篇,道理根本就说不到点子上。

黄章晋在这里用《读者》来作例子,后面开始谈论《狼图腾》。对于没看过《读者》或《狼图腾》的人来说,还可以参考各种所谓的心灵鸡汤。这样的故事和道理在网上漫天飞舞,一抓一大把,一不小心还会飞进自己的邮箱。对于这样的文字,我一般的作法是直接删除。觉得不爽的时候,就用黄章晋的这段话reply。
对于年幼者或智力低下者,有时讲故事比讲道理要更容易理解一些。但是我相信对于一个智力正常的成年人,使用比喻之类的手法来传达思想,实在是一种有辱智商的行为。


2005年2月3日星期四

微软发布MSN Search

本周微软发布了自己的搜索引擎MSN Search,这是微软潜心于搜索引擎多年以后的成果,其中也有不少是北京的微软亚洲研究院的功劳。这是微软用于挑战Google的武器。
这两天中,在公司内的各个角落,办公室、电梯、食堂、车辆,全都是MSN Search的广告。
试用了一下,MSN Search也的确有自己的过人之处。除了新闻,音乐,图像,桌面搜索这些google也有的东西以外,MSN Search还支持对于微软自己的百科全书软件Encarta的搜索。
另外,有一个叫Near Me的搜索模式,可以返回和用户的地理位置比较接近的页面。
还有一个叫做Search Builder的工具,可以帮助用户创建复杂的搜索条件,而无需去记忆那些复杂的搜索命令。
可惜的是,MSN Search还不支持中文界面,但是用中文搜索是没有问题的。

和MSN Search相关的几个页面:
这个是MSN Search的首页:http://search.msn.com/
这个是MSN Search的blog:http://blogs.msdn.com/msnsearch
这个是MSN Sandbox,有些新鲜玩意,当然也包括MSN Search:http://sandbox.msn.com/
这里有MSN Search新特性的介绍:http://www.imagine-msn.com/search/tour/moreprecise.aspx

2005年2月2日星期三

Good Luck Killer

早上跑完步以后回家,在condo附近见到一只灰色的猫。
猫走得不紧不慢,很悠闲的样子。尾巴直直的竖起来,那是猫心情很好的表现。
我蹲下来,把手伸向她,撮着嘴挤了点声音出来吸引她的注意力。
那猫立刻小跑到我的身边来,闻了闻我的手,然后用头颈蹭了蹭的手。我怀疑她饿了。抚摸她的身体的时候,发现她很胖,看来是从来没有挨过饿。她的脖子上戴着一个项圈,上面还有一个小铭牌。
她围着我转了两圈,就向我的身后走了。
我站起来,四下看了看,周围没有人。我心里觉得有些不妥,看来那是一只流浪猫,而且刚刚开始流浪。
于是我追了上去,叫住那只猫,然后把她抱了起来。真的很沉啊,身体软软的,好象没有骨头。
猫开始挣扎了一下,然后就老老实实地仰面躺在我怀里了。
我看了看她的铭牌,上面写着“Killer”,还有一串本地的电话号码。
^_^,这么肥胖又温顺的猫,居然叫这个名字。
然后这时,我身后开来了一辆汽车。Killer被吓了一跳,从我怀里一下窜了出去,跑开了。
我赶紧在心里重复了两次那个在铭牌上的电话号码。然后回condo去,拨了那个号码。
是个男人接的电话。我告诉他,我在附近看到了他的猫Killer,然后告诉他地址。那人说马上过来。
我挂了电话之后去洗澡。刚洗完澡出来,就听到楼下有人喊“Anyone found my cat?”。我赶紧穿上衣服下楼,告诉他是我打的电话,然后领他到Killer最后离开我的地方。
我问他,“Is it she or he?”他说,“It is he.”^_^
他说他会找到Killer的。我说“good luck”,然后转身离开。
回头的时候看到,他还在那一带搜索,嘴里念着“KillerKillerKillerKillerKillerKiller”
我想到Killer的发音和Kitty有点象,这应该是英语里很常见的猫名吧。
等我上楼收拾好东西,出发去公司的时候,那个人已经不见了。大概已经找到他的Killer了吧。

走失的宠物猫对于猫主人、猫自己和周围的环境来说,都是很痛苦的。所以希望每一个养有猫的人,照顾好自己的猫,给她配上铭牌,留下自己的联系电话。也希望发现流浪猫的人,能尽可能的帮助她回到她该去的地方。

很遗憾,我没有留下Killer的照片。