Daiyi 的个人资料Simple Life照片日志列表更多 工具 帮助

日志


2008/1/24

Jeffrey Ritcher的一堂课

花了两天时间,听了Jeff的.NET Threading in C#,受益非浅。
原本参加这个培训是希望见识大师风采,不过学到的东西更是超出自己的预期。回头看看自己以前写的代码,很多地方都值得商榷。
很佩服Jeff在连续讲了4天之后,还能如此热情和耐心地解答大家的问题,给我留下最深刻的记忆,不是技术,而是他这么一段话:
It's poor to write books, which will NOT bring you much money indeed. It requires you staying alone in a room, without talking to anybody, but just typing. Indeed, delivering training like today earns much more money. But I choose to write books, because it could help people, could make impact to the world, I am proud of what I have done, it helps people to write better programs, to make the world better...
希望自己在二十年后,也还能保份赤子之心。
2006/7/13

设计的艺术

Plato曾说过,知识是发现已有的东西,而艺术是创造没有的东西。
 
上帝造就的大千世界,人类发明的新奇事物,都是艺术。但艺术有狭义和广义之分,广义的艺术存在于所有的创造物中,它是一种泛泛的存在形式;而狭义的艺术,也是我们通常所指的“艺术”,存在于美和善之中,它是一种多数派的认同。
 
认同来自于经验、知识;因此,我们可以说,艺术是人类对“美”的经验和知识认同和肯定的创造性劳动。知识和经验是不断增长的,因此,艺术是无限的。
 
什么是设计的艺术?我在多年前就问自己。也许今天的我仍难参透设计艺术的真谛,但我却朦胧感觉到了好的设计带给大脑的美感和心灵的震撼。设计的美感在于,在满足需求的同时,做到 close to modify, open to extend。欣赏美容易,创造美难。在过去几个月的工作中,我慢慢地发现了设计的几条重要原则,今天总结出来和大家分享:
 
1、一致:概念是设计的灵魂,一个好的设计不仅能满足客户的需求,而且要能体现概念的一致。它可以包含很多细节,但在宏观上,它的概念必须紧凑,并且统一。这并不是运用几条Design Pattern就能达到的境界,它的关键在于审慎地分析需求以及实现High Level的抽象。
 
2、强制:好的设计必须具有强制性,这并不是说将设计实现者的工作机械化。而是为实现者的行为规约,即:实现者必须理解设计、正确履行设计,才能实现目标。这能够将由于个体能力差异造成的风险降至最低,同时良好的系统分层能够减少培训的成本。通常情况下,设计只是定义了系统交互的接口或机制。具体的实现工作将富有挑战性和创造性。
 
3、适度:好的设计在于适度。设计就像说话,说太少让别人去猜,说太多就变成了唐僧,不仅说的人辛苦,听的人也痛苦。设计不足和过度设计就是两个极端。这个平衡需要经验来掌握,另外,就是利用良好的开发过程(例如Agile)来进行规约。
 
4、不因“恶”小而为之,不因“善”小而不为:设计往往在一段时间的演化后失去它本来的味道。原本香喷喷的一道菜很可能后来放入了不恰当的佐料而变成“四不像”。很喜欢Martin Flower用Bad smell来形容破坏系统一致性和协调性的代码。要防止设计的变质,我们必须做到不因”恶“小而为之,不因“善”小而不为。即,每次对设计的改动必须被反复的复审:是否破坏了设计的一致性?是否让某部分的设计产生了歧异?
 
对程序员们的建议
 
如果你想成为一个设计者,你首先要做到下面一些方面:
 
1. 词要达意:在设计类、接口和方法名称时,一定要体现设计者的真正意图。我曾经有过花20分钟想一个类名的经历。如果有一天,你偶然发现自己对类的命名和系统库相同时,就说明这点你做到了:)
 
2. 具有大局观:很多程序员之所以无法超越实现者而成为设计者,往往在于他的眼睛习惯于看细节,对系统的演变缺乏嗅觉。培养大局观,就是要通过分析搞清楚,哪些是必须在设计之初就确定的,哪些是在实现的过程中再考虑的。项目是一个动态的过程,但其核心目标不会变。设计也一样,虽然随着需求进行调整,但却有其持久不变的东西。当这种持久不变的东西和需求冲突时,考虑需求是否在形式上可以改进。
 
3.开阔的视野和广博的射猎:设计者不能想当然。他必须对设计是否能实现目标有着清楚的认识。这就要求设计者具有开阔的视野和广博的射猎。不要再把问题局限在“如何实现Zip文件的解压”这类细节的问题上,你应该花更多时间去看看技术发展的方向和那些被给予高度评价的开源项目。
 
设计者的乐趣
 
相信我,设计者的乐趣要远远大于一个实现者。抛开功利,设计带给人单纯的创造事物的快乐。
苏格拉底两千多年前就指出了最大的幸福是玄想。
设计,这个最接近玄想的工作,也成了我最大的幸福源泉之一。
 
2006/6/14

冰火两重天 (Posted on 2006/06/08 13:55)

职业的乐趣

编程为什么有趣?作为回报,它的从业者期望得到什么样的快乐?
首先是一种创建事物的纯粹快乐。如同小孩在玩泥巴时感到愉快一样,成年人喜欢创建事物,特别是自己进行设计。我想这种快乐是上帝创造世界的折射,一种呈现在每片独特、崭新的树叶和雪花上的喜悦。
其次,快乐来自于开发对其他人有用的东西。内心深处,我们期望其他人使用我们的劳动成果,并能对他们有所帮助。从这个方面,这同小孩用粘土为“爸爸办公室”捏制铅笔盒没有本质的区别。
第三是整个过程体现出魔术般的力量——将相互啮合的零部件组装在一起,看到它们精妙地运行,得到预先所希望的结果。比起弹珠游戏或点唱机所具有的迷人魅力,程序化的计算机毫不逊色。
第四是学习的乐趣,来自于这项工作的非重复特性。人们所面临的问题,在某个或其它方面总有些不同。因而解决问题的人可以从中学习新的事物:有时是实践上的,有时是理论上的,或者兼而有之。
最后,乐趣还来自于工作在如此易于驾驭的介质上。程序员,就像诗人一样,几乎仅仅工作在单纯的思考中。程序员凭空地运用自己的想象,来建造自己的“城堡”。很少有这样的介质——创造的方式如此得灵活,如此得易于精炼和重建,如此得容易实现概念上的设想。(不过我们将会看到,容易驾驭的特性也有它自己的问题)
然而程序毕竟同诗歌不同,它是实实在在的东西;可以移动和运行,能独立产生可见的输出;能打印结果,绘制图形,发出声音,移动支架。神话和传说中的魔术在我们的时代已变成了现实。在键盘上键入正确的咒语,屏幕会活动、变幻,显示出前所未有的或是已经存在的事物。
编程非常有趣,在于它不仅满足了我们内心深处进行创造的渴望,而且还愉悦了每个人内在的情感。

职业的苦恼

然而这个过程并不全都是喜悦。我们只有事先了解一些编程固有的烦恼,这样,当它们真的出现时,才能更加坦然地面对。
首先,必须追求完美。因为计算机也是以这样的方式来变戏法:如果咒语中的一个字符、一个停顿,没有与正确的形式一致,魔术就不会出现。(现实中,很少的人类活动要求完美,所以人类对它本来就不习惯。)实际上,我认为学习编程的最困难部分,是将做事的方式往追求完美的方向调整。
其次,是由他人来设定目标,供给资源,提供信息。编程人员很少能控制工作环境和工作目标。用管理的术语来说,个人的权威和他所承担的责任是不相配的。不过,似乎在所有的领域中,对要完成的工作,很少能提供与责任相一致的正式权威。而现实情况中,实际(相对于正式)的权威来自于每次任务的完成。
对于系统编程人员而言,对其他人的依赖是一件非常痛苦的事情。他依靠其他人的程序,而往往这些程序设计得并不合理,实现拙劣,发布不完整(没有源代码或测试用例),或者文档记录得很糟。所以,系统编程人员不得不花费时间去研究和修改,而它们在理想情况下本应该是可靠完整的。
下一个烦恼——概念性设计是有趣的,但寻找琐碎的bug却只是一项重复性的活动。伴随着创造性活动的,往往是枯燥沉闷的时间和艰苦的劳动。程序编制工作也不例外。
另外,人们发现调试和查错往往是线性收敛的,或者更糟糕的是,具有二次方的复杂度。结果,测试一拖再拖,寻找最后一个错误比第一个错误将花费更多的时间。
最后一个苦恼,有时也是一种无奈——当投入了大量辛苦的劳动,产品在即将完成或者终于完成的时候,却已显得陈旧过时。可能是同事和竞争对手已在追逐新的、更好的构思;也许替代方案不仅仅是在构思,而且已经在安排了。
现实情况比上面所说的通常要好一些。当产品开发完成时,更优秀的新产品通常还不能投入使用,而仅仅是为大家谈论而已。另外,它同样需要数月的开发时间。事实上,只有实际需要时,才会用到最新的设想,因为所实现的系统已经能满足要求,体现了回报。
诚然,产品开发所基于的技术在不断地进步。一旦设计被冻结,在概念上就已经开始陈旧了。不过,实际产品需要一步一步按阶段实现。实现落后与否的判断应根据其它已有的系统,而不是未实现的概念。因此,我们所面临的挑战和任务是在现有的时间和有效的资源范围内,寻找解决实际问题的切实可行方案。


这就是编程。一个许多人痛苦挣扎的焦油坑以及一种乐趣和苦恼共存的创造性活动。对于许多人而言,其中的乐趣远大于苦恼。

[节选自《人月神话》]

The Mythical Man-Month (Posted on 2006/06/06 09:15)

有时候感觉team处在一个焦油坑中,越是猛烈地挣扎,越是摆脱不了下沉的困境。

以前从来没有意识到影响项目成功的最大要素,直到自己参与到一个较大型的Project中来。在过去的日子里,自己往往能凭着一己之力贯彻从需求分析到项目部署的全部过程。需求,不外乎是对系统理解的内伸和外延罢了。因为那时候,完美主义的我总会尽力将系统设计得尽量flexiable。对于小项目,这是可以接受的,因为稍微复杂的设计并不会给小系统增加多少复杂度。但是对于中等、大规模项目,over design将让大家难以接受,both cost of time and cost of human resource.

在大学的日子里,需求分析在追求技术含量和设计美感的偏执下被贬低得一文不值。大家往往是谈MIS色变,MIS也成为了没有技术含量的代名词。然而,工作后慢慢发现,真正阻碍一个项目走向成功的,并不是对技术把握的不足或是系统设计的缺陷,而是对需求分析的漠视或者需求变化的无常。我们可以很好地管理代码,也可以在技术框架下让程序员服从于设计,但我们不能让客户停止他们跳耀着的思维。毕竟而今眼目下,市场是检验项目成功的唯一标准。

需求像一种无形的手,它驱使着一个个天才的大脑去做一件件无奈的事情。无奈不仅体现在不断重复已有成功的boring,更体现在它的变化无常让人琢磨不透。

需求可以变化,但是作为一个好的PM,更能将它的变化纳入自己的计划,让它可控地变化,让它每一次变化都落入我们的“陷阱”之中。同时,对于过于随意的突发奇想,我们应该理直气壮地说No。一个成功的项目,不仅在于她实现了多少需求,更在于她合理地拒绝需求。

大象无形,大音无声。设计的美感来自于包容,需求的艺术在于接受和改变的平衡。

浅谈代码生成 (Posted on 2006/05/25 16:03)

今年年初,我花了大概2个月的时间设计和实现了一个代码生成框架-CGE(the Code Generation Engine)。现在想想这个名字,真不具备任何的意义,本来想改成MetaForge,但是boss觉得大家对CGE这个名字已经耳熟能详了,加之念起来也琅琅上口,于是保住了CGE的名号。

这段时间,随着CRM Team逐渐成熟,我已经能脱离CRM Team而重新回到了“思想者”的位子。虽然还有Verticle Search Engine等需要研究和探索的问题,我却也有了相对充裕的时间来对CGE在过去3个月中对CRM Team开发过程的影响做一些总结。关于代码生成框架的再思考一方面来自于自己的一些新想法,更多的应该是得益于CGE在CRM开发过程中的应用经验。 现在是时候重新改造CGE了,今天将自己对代码生成的理解陈列一番,一是希望总结一下这半年来的经验,二是希望大家能有新的思路和见解。

在开始CGE Project之初,心里是没底的。最高目标深不可测,最低目标倒比较实际和具体。启动CGE的初衷是提供一套需求描述语言,并在需求的基础上实现Java Bean Class的自动生成、Hibernate映射文件(hbm)的自动生成、以及数据库的自动生成。归纳一下,就是至少能实现MVC框架中Model的自动化生成。其实有了hbm,通过Hibernate tools,我们能够方便地得到数据库的DDL。

实现最低目标是比较容易的,因为Java Bean Class的信息已经能被UML完备地描述,我们只需要在UML加一些辅助信息,就能生成hbm。但是,大概花了一周,在做完了第一个实验品并顺利地实现目标之后,我却将之前的设计全盘推翻。原因是,在基于XML的CGE Model Document中,UML主干信息和辅助信息紧密地交织在一起,又由于辅助信息会根据需求的变更快速地膨胀,这使得CGE Model要么难以用Xml Schema来规约,要么难以在将来根据需求扩展。

在反复的思考后,我选择了Meta-meta模型来设计CGE Model,所谓Meta-meta,即双元模型。这其实是我在思考CGE设计时自己想到的词汇,没想到后来在Google上还搜到了这个词,一直相信词汇(或命名)的运用是体现设计者大局观的一个视角,我确实内心窃息了一阵:)。现在先来解释为什么是Meta-meta吧,第一个meta指的是UML结构的元信息,它描述了对象的数据和逻辑组成;第二个meta指的是对UML元素的修饰信息。做一个比喻,前一个meta体像是树干和树枝,而第二个meta却像是叶子和树枝上的悬挂物。前者的形式是固定的,而后者的形式却是变化的。前者可以用DTD或者XSD来规约,而后者却因为不受规约的影响能自由地扩展。后来,经过进一步的思考,我引入了Meta-schema的概念,用来规约第二个meta。这是一个未能完全实现的概念,它描述了什么样的叶子可以用于什么样的主干,并定义了一系列的高级特性,例如meta的命名空间,meta的默认值,meta的继承等等。

我后来将CGE分成了三层:核心层(Core)、方法层(Methodology)和业务层(Business)。

核心层囊括了Model的定义,Meta的规约以及模型元素的查询。它实际上提供了一个在对象的网络里挖掘信息的机制。

方法层是什么?方法层将二维的世界分解成两个一维的世界,二维的世界是由横纵两个侧面来定义的,横是软件项目的规模,通常认为是具有相同处理方法的不同实体的个数,我们记为m;纵是做一类事物的所需要的步骤,通常认为实现某种类型的逻辑所需要的社会平均时间,我们记为n。传统的软件开发过程通常需要m×n的代价才能完成,而CGE的方法层将纵向的方法抽象出来,在核心层建模的基础上,将整个项目的代价变为m+n。在CGE的开发过程中,m为建模所需要的时间,而n则为探索以及泛化一类事物的解决方法的时间,不难理解,方法层所需要提供的输入是文件模板和核心层的模型,输出是对于模型中的每一个对象,采用定义好的方法为其产生输出。

业务层是建立在方法层的上层,它直接面向用户,提供一系列的工具和模板方便用户建立自己的解决方案,它可以是一组自定义元数据(meta)、一组文件模板以及一组外部资源的集合。它直接面向具体类型的项目,为它们提供自动化代码生成的解决方案。

随着最近几年AOP的迅速发展,声明式(Declarative)程序编写方法被大量地应用。它有效地利用了语言反射机制,为我们提供了一套根据接口来请求服务的途径。我将AOP中常见的设计模式IOC(Inversion of Control,也叫Component Injection)引用到代码生成的过程中,而我们只需要加入新的Meta,就能够在代码生成过程中控制类所需实现的接口。例如,在我们CRM项目中用到了crm:enable-search这个meta(它的前缀crm类似xml中的命名控件前缀),这个meta悬挂于类的Member field,它告知我们的业务层,我需要将这个字段加到高级查询的条件列表中去。这样在我们生成类定义的时候,会检查该类的模型中是否有成员变量申明了这个meta,如果有,则让该类实现Searchable接口,并自动生成Searchable接口的getSearchEntries方法对于该类的实现。

CGE虽然已经能为我们做很多事,但仍有很长的一段路要走。虽然在成都的日子已经不多了,但我想,无论是出于工作还是出于兴趣,我都会像照顾自己的孩子一样将这个项目完成,并尽力地将其贡献给开源社区(由于过去的工作是所在公司的一部分,我需要获得boss的许可)。

下图是CGE Project的发展方向,真希望有人能和我一起去实现她……



附上半年来对我的思考有着重大意义的一些只言片语,与大家共勉:

Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius—and a lot of courage—to move in the opposite direction.
Albert Einstein

If you have built castles in the air, your work need not be lost; that is where they should be. Now put the foundations under them.
Henry David Thoreau

I have the world’s largest seashell collection. You may have seen it. I keep it spread out on beaches all over the world.
Steven Wright

iframe的Document属性 (Posted on 2006/04/20 16:24)

在DHTML中,我们都应该很熟悉iframe.document属性,而iframe.Document(注意哦,是大写的D)属性却很少被提及。
 
iframe.document指向的是其所在的文档对象
而iframe.Document指向的是该iframe内部包含文档对象。
 
差之毫厘,谬以千里。切记切记……