2009年6月12日星期五
《彩色UML建模》译者序
开发者希望看到真实世界中企业级开发的例子,而不只是ATM机的UML图。如果您是一名建筑设计师,您有机会看到金茂大厦的设计图纸吗?我们会看到最后的产品,但对产品得到的过程一无所知。模型并不重要,重要的是得到模型的过程。我们希望听到更多的设计大师的自战解说。开源让我们有机会看到优秀的代码,而这本书让我们有机会看到优秀的面向对象分析模型,以及建模的过程。
建模能帮您做到什么?或者说,您为什么要建模?对我来说,建模是为了让软件开发更简单一些。虽然让复杂的事情变得简单并不容易,但这本书却做到了。通过4种彩色的架构型,让我们能够迅速地对复杂的业务领域建立起简单的模型。
系统分析师需要“体察千行百业之要义”,这本书对企业组件的全景式描述,为我们提供了学习的典范。
Booch在他的《面向对象分析与设计》一书中说,所有成功的软件项目都有两个显著的特点:一是很强的架构愿景,二是迭代增量式的开发。彩色UML和FDD做到了这两点。
一本好书会改变您对事物的理解,从而改变您的行为实践。对我来说,这本书就是这样的。每次我看到其他人给出的领域分析模型,都会用彩色UML的方法去印证,结果总是能得到更好的模型。
这本书是一本值得反复临写的贴,是一本值得反复打的谱。
在这本书的翻译过程中,我学到了很多,因此郑重地向大家推荐它。如果这本书对于您改进软件开发实践有所帮助,我将十分高兴。
2009年6月10日星期三
Top down还是Bottom up?
一个朋友曾对我说,很感谢父亲小时候对他的作文训练。训练的方法是这样的:扩写和缩写。把一句话扩写成一段话、一篇文章。把一篇文章缩写成一段话、一句话。这就是所谓的演绎和归纳,也是自顶向下和自底向上的方法。
Alistair Cockburn在《编写有效用例》时也表达了这样的观点:所有的系统都可以归纳为一个最大粒度的用例,即“使用XX系统”。然后我们再把它逐步细化,得到“天空级用例”、“海平面级用例”、“水下级用例”。
这是自顶向下的分析方法。但也有不同观点。
Martin Fowler在《设计已死?》中传达出这样的观点:告诉我一个一个的用户故事,最后我会给你一本故事集。这本故事集就是它存在的意义所在,你不必事先告诉我这本故事集“一言以蔽之”应该怎么说。我可以通过不断的消除重复和重构,得到高级的抽象概念。
但是Grady Booch在《面向对象分析与设计》中告诉我们,人们对客观世界的认识往往是从中间的抽象概念开始的。例如小孩在认识动物时,开始是认识狗,然后会向下认识拉布拉多、吉娃娃,向上认识哺乳动物、动物。
我们在写文章时也是如此。先会有一段素材,让你在脑子里闪过一个念头,涌出一股写作的冲动。然后你开始仔细思考,提练更深刻的主题,寻找更多的素材,向抽象和具象不断探索。
我在设计系统时也是如此。
2009年6月9日星期二
RESTful的codebase
毫无疑问,代码是很多人关注的一项资源,而资源可以用RESTful的方式来表现。所以可以有这样的URL:
http://www.google.com/code/com.google.inject
http://www.google.com/code/com.google.inject.Guice.java
http://www.google.com/code/com.google.inject.Guice.java/revisions
http://www.google.com/code/com.google.inject.Guice.java/authors
这样隐藏了代码集的一些实现细节,例如,它使用什么版本控制工具。
对代码的浏览可以有SourceInsight那样的效果,也就是把SourceInsight放到云端。
从此不再需要每个人都从svn下载一份代码拷贝,然后打开SourceInsight来读代码了。
爱因斯坦说:想象力比知识更重要。
熊彼特说:创新,就是新的资源组合方式。
RESTful的课程表
如果按RESTful的风格进行设计,可以考虑这样的URL:
http://www.HealthIsOne.com/schedules
http://www.HealthIsOne.com/shanghai/citychamber/schedule
http://www.HealthIsOne.com/coaches/troy.zhu/
http://www.HealthIsOne.com/shanghai/citychamber/rooms/room2
http://www.HealthIsOne.com/courses/YugaIntro
返回的内容可以协商,根据不同情况返回HTML、XML、JSON、PDF或图片,也可以有这样的URL:
http://www.HealthIsOne.com/coaches/troy.zhu/photo
http://www.HealthIsOne.com/coaches/photos
http://www.HealthIsOne.com/shanghai/citychamber/coaches/photos
课程表会变更,就意味着有历史课程表,所以可以有这样的URL:
http://www.HealthIsOne.com/shanghai/citychamber/schedule/20080305
这样做的好处在于:
1、以业务资源为中心,隐藏一切实现细节
2、应用和数据回归Web本质
3、容易支持扩展
4、容易实现缓存机制
2009年6月7日星期日
临摩
就这样几年之后,儿子写的字有点像样子了。汉隶临的是《张迁碑》,行书在临《兰亭序》和《集字圣教序》。诗云:如切如磋,如琢如磨,大概就是这个意思。书法艺术有几千年的历史,从二王、索靖、张芝、智永、欧储颜柳、张旭、怀素,到苏黄米蔡、赵孟頫、徐渭、王铎,出现了为数不少的大师。不仅他们的作品值得我们反复临习,他们成为大师的艺术追求之路,也同样值得我们借鉴。
米芾的例子就很典型。米芾习书,自称“集古字”。根据米芾自述,在听从苏东坡学习晋书以前,大致可以看出他受五位唐人的影响最深:颜真卿、欧阳询、褚遂良、沈传师、段季展。米芾有很多特殊的笔法,如“门”字右角的圆转、竖钩的陡起以及蟹爪钩等,都集自颜之行书;外形竦削的体势,当来自欧字的模仿,并保持了相当长的一段时间;沈传师的行书面目或与褚遂良相似;米芾大字学段季展,“独有四面”、“刷字”也许来源于此;褚遂良的用笔最富变化,结体也最为生动,合米芾的脾胃,曾赞其字,“如熟驭阵马,举动随人,而别有一种骄色”。然后他开始学习二王。《宋史》载:米芾“妙于翰墨,沉著飞翥,得王献之笔意;尤工临移,至乱真,不可辨……”。最后,“既老始自成家,人见之,不知何以为主”,完成了自己风格的确立,大概在五十岁以后。
所以,成为书法家的过程可以总结为四个字:入帖、出帖。
张大千则是画家中的典型例子。他早年临石涛临到乱真,号称“当代石涛”。四十多岁时自费赴敦煌,花了大量时间对十六国、北魏、北周、隋、唐、五代、宋、西夏、元各朝的壁画代表作及雕塑进行了临摹。从此,张大千的画风也为之一变,善用复笔重色,高雅华丽,潇洒磅礴,被誉为“画中李白”、“今日中国之画仙”。他走的路和米芾一样:从临摩开始,到乱真,最后自成一家。
我读中学时,正好是聂卫平在中日围棋擂台赛上大胜的时候。全国上下掀起了一阵围棋热。我也去弄了一本电视围棋教程来看,我记得很清楚,我只看到了第37页,就把书一扔,找人对杀去了。杀是杀得很爽,但我那时连征子都还没学到,实战中另一个同学“教育”了我。围棋也有数千年的历史,王积薪、黄龙士、施范、秀策、木谷、吴、六超、聂马、曹李,都是我们可以效仿的大师。
一般来说,要想提高棋力,需要日复一日地做三件事:打谱、做死活题、实战。打谱是一种临摩,做死活题是一种临摩,实战也是一种形式的临摩。
另一点值得强调:实战一定要复盘。不论结果如何,要找出下得好的棋和下得臭的棋。在水平低的时候,如果有条件,最好是找水平高的老师来帮忙复盘。这就是老师的作用:虽然他的水平没有大师那么高,但是他可以告诉你练习的方法(需要去临摩哪些东西),也可以指出你最需要改进的地方。可惜我现在明白了怎么学习围棋,却没有太多的时间去练了。
如果想学习管理,可以去看看朱兰、克劳斯比、戴明、德鲁克、高德拉特、韦尔奇,再找个咨询师。如果想练习游泳,可以去看看波波夫、北岛康介、菲尔普斯的视频,再找个专业教练。
如果您是设计软件的,梦想成为一名伟大的程序员,那该怎么做?您一定已经知道了。
2009年6月6日星期六
面向对象设计与开发(两天培训提纲)
- 高内聚、低耦合原则
- 关注点分离原则
- SOLID原则
- Don’t Repeat Yourself!
- 加入一个中间层!
- 复杂系统的架构方式
二、设计模式
- 设计模式的意义
- 设计模式的模板
- 设计模式选讲
三、面向对象系统开发过程
- 从需求到OOA
- OOA到OOD
- 自动化测试和持续集成
- 敏捷开发方法学
四、领域分析与建模
- 领域驱动开发
- 彩色UML方法
- 案例分析
五、相关技术
- 对象持久设计
- 表示层设计
- 组件化
- AOP
- SOA和SaaS
- RESTful和AJAX
- MapReduce集群
2009年6月4日星期四
《执行SOA》上市了
译者序
几年前,为了尝试JDK 1.5中的并发包,我写了一个多线程的网页爬虫程序,利用线程池来抓取和分析页面。
并发200个线程,每个线程从待爬URL队列中取得一个URL,取回网页,进行分析,找出其中的URL链接,再放入待爬队列。开发过程很正常,但在测试中遇到了问题。在爬了7万多个网页之后,程序开始越来越慢。凭感觉判断,有一些线程“死”掉了。
多线程的调试并不是件容易的事。这个问题很“难”再现。这不是普通意义上的难再现,它每次都会出现。但要跑到7万多URL时,才会出现。也就是说,再现这个问题的代价很大。我试过将线程池的大小退化到 1,想找出什么样的URL会导致线程死掉,但是行不通,因为速度太慢。当时的IDE也缺乏对多线程调试的一些支持。而且即便有支持,可能也不太适合这种情况。后来因为种种原因,那个程序就不了了之了。
这本书中SOA治理的思想给了我一些启发:我们需要关注服务执行的健康状况,包括服务执行的时间。例如,我们可以进行这样的改动:
在每个线程领取URL时,记录一个时间戳。在它完成这个URL处理时,再记录一个时间戳。再利用一个线程,对未完成的URL定时检查它的健康程度。如果在很长的一段时间内它还没完成,那么它就有问题。这样我们可以找到嫌疑URL。我们可以对这种URL单独测试,看看是否因为程序的原因,不能处理这样的URL。或者,我们可以把对应的线程任务杀掉,直接跳过这些有问题的URL。
如果您和我一样,是一名开发人员,学习一些SOA的思想是很有帮助的。我们可以在程序中设计一些机制,支持运营维护和故障分析,这正是SOA的一部分内容。
IT运维部门需要SOA。业务部门需要SOA。企业高层需要SOA。设想一家经营固话业务的电信公司,通过兼并和重组,拿到了一个移动网络。公司最需要的是什么?就是SOA。
这个移动网络上跑着多少应用?多少中间件?多少数据库?多少操作系统?多少服务器?它们的使用状况如何?它们由谁提供技术支持?它们是什么配置和版本?它们有哪些参数可以调整?它们支持着怎样的业务流程?它们支持着怎样的业务数据模型?它们提供怎样的QoS?它们在安全性和可伸缩性方面存在哪些风险?
SOA参考框架帮助我们提出这些问题。提出问题比解决问题更重要,真的。企业应该认真考虑向SOA迁移。
2009年5月24日星期日
改进实现
- 每睡一小时/一分钟都会输出一条日志,一个周末下来,输出了很多重复的日志。
- 端午节要到了,原来的实现只考虑了周末,没有考虑这种节假日休息,周日反而要工作的情况。
- 在WorkTimeControllerImpl中添加一个方法“Date nextStartTime()”,计算下一次开始工作的时间,这样waitTillStartTime()就可以只输出一条日志,然后一直睡到下次开始工作。
- 在WorkTimeControllerImpl中添加一个非工作日列表,包括正常周末和节假日。
- 重复的无用信息是不好的。Don't Repeat Yourself. 无用的信息会干扰有用的信息。
- WorkTimeController和TimeMachine接口都不需要改变,这就是区分“做什么”和“怎么做”的好处。
2009年5月21日星期四
TimeMachine
所以我设计了一个WorkTimeController,它有3个方法:
void waitTillStartTime();
void skipLunchTime();
boolean afterEndTime();
waitTillStartTime()负责跳过双休日、国定假日,直到工作日的9:00为止。它在8:00之前,会sleep(ONE_HOUR),再检查一下时间。在8:00之后,会sleep(ONE_MINUTE),再检查一下时间。
skipLunchTime()在工作时间会立即返回,在午休时间会sleep(ONE_MINUTE),再检查一下时间。
afterEndTime()判断是否在15:30以后了。
工作程序有一个doOneDayJob()方法:
void doOneDayJob() {
workTimeController.waitTillStartTime();
while (true) {
doSomeJob();
workTimeController.skipLunchTime();
if(workTimeController.afterEndTime()) {
break;
}
}
}
这样就完成了一天的工作。
现在的问题是,如何来验证这些时间控制动作都是正确的?难道只有让时间来证明?“Time, goes by, so slowly...”
于是我设计了一个TimeMachine接口,包含两个方法:
long currentTimeMillis();
void sleep(long time);
WorkTimeController使用TimeMachine来获取当前时间和休息。
TimeMachine的正常实现是很直接的,我设计它的目的在于,可以实现一个MockTimeMachine。这个MockTimeMachine的sleep方法什么也不做,立即返回。而它的currentTimeMillis方法则依次返回一组预先设定好的时间。这样,我们就得到了一个时光机,可以检测在一些关键的时间点程序行为是否正常。
时间是一个关注点,在这个例子里,我们实现了时间关注点的分离,改善了程序的可测试性。
2009年5月14日星期四
彩色UML总能给出漂亮的领域模型
手上在写的一个程序需要不断抓取实时信息,然后根据既定的策略做出相应的反应。写着写着,就觉得结构有点乱了。等一下!我还没有用彩色UML建过领域模型!
2009年5月10日星期日
最大价值
2009年5月6日星期三
美丽架构7原则
- 一处一事实。一个设计决定,只出现在一个地方。将来当这个设计决定改变时,改动量可以最少。参见设计决定、反悔、霰弹式修改和架构污染。
- 自动传播。有时候出于效率考虑,必须复制一些东西。系统需要保证这些复制很容易自动进行。现在流行的分布式版本控制系统(Git、Hg)就是很好的例子。还记得当初的EJB吗?事实是我们在写分布式应用,然后规范要求我们在几个地方尊重这个事实。EJB3.0改变了这种情况。
- 架构也包含构建过程。架构什么都不是,围绕架构的过程就是一切。
- 使用最少的机制。够用就好。要明确目标,优化瓶颈,不要沉迷于非瓶颈部分的优化。要事第一。
- 设计引擎。利用引擎,我们把该放在一个地方的内容放在一个地方。例子有规则引擎、工作流引擎、脚本语言和DSL。但是在项目中自己实现一个引擎要考虑实现的成本和项目的经费。
- 支持伸缩。在负载增大的情况下,系统的表现如何?系统是怎样的方式实现伸缩?
- 抵制熵增。年轻时很美并不难,难的是一辈子到老都很美。美丽的架构能够经受时间的考验。
2009年5月4日星期一
DSL
Google:广告界最懂信息技术的公司
2009年4月29日星期三
Endless Test, Continuous Integration
- 让客户确定里程碑。
- 写出业务测试用例。
- 扫除一切障碍,实现业务用例。
2009年4月24日星期五
改造多线程爬虫
- 从一个待爬集合中取一个待爬URL
- 取回这个URL代表的Web页面
- 对页面进行解析,找出其中的链接URL
- 如果找到的URL不在已爬集合中,就把它放到待爬集合中去
2009年4月18日星期六
多线程、Project Darkstar、MapReduce和GAE
2009年4月15日星期三
2009年4月14日星期二
“大”项目的关键是集成
日志设计
为什么要记日志?为了在某个时候以某种方式对日志进行分析。Tim Bray在《代码之美》的第4章中,用例子说明了如何分析他的Blog的访问日志,找出最受欢迎的10篇文章。
从本质上来说,日志记录了系统中发生的事件。我们应该将重要业务事件的处理记入日志,并通过记录事件ID,保持事件的可追踪性。
例如,在一个filter-pipe架构中,每一条信息都有一个ID,每一个filter对信息的处理(通过或过滤)都记入日志(What)。在需要时,还可以记下过滤的规则(Why)。由于日志会记录时间,我们就可以写一些分析程序/脚本,统计业务事件的发生情况和处理时间。又是5Ws and an H的思想。
2009年4月12日星期日
Filter和Pipe是一种实用的架构
2009年4月11日星期六
7个习惯
2009年4月2日星期四
低配置机器上的开发环境
- Mercurial HG:分布式版本控制系统。
- JDK:写Java程序,没这个不行。可能还要装Java API doc。
- vim:带行号显示、语法加亮的编辑器总要一个的。
- Ant:经常要打ant clean test。
怎样才能提高自己的能力?
子曰:“大哉问!”凡是练功升级的人,都在思考这个问题。一般来说,我们需要:学习,思考,实践。
- 学习是摆在第一位的。“吾尝终日而思矣,不如不如须臾之所学也”。“三人行,必有我师焉。择其善者而从之,其不善者而改之。”要师从古人,师从外国人,师从周围的人。
- 学习完了要自己思考。“学而不思则罔,思而不学则殆”。他人的观点纷繁复杂,有的甚至针锋相对。思考的目的就是在自己头脑中建立起一个自洽的知识体系,将新学到的知识与旧的知识融合,使之相互得到加强。如果一个人,以前对UML建模很有研究,后来又醉心于敏捷方法学,然而他不能将这二者很好地融合,那么他只能算是一个跟风者,就像那只过玉米地的猴子。我怀疑这些知识是否真正成为他的东西。
- 接下来是实践,实践出真知。实践可以检验你的理解是否正确,即是否符合客观规律。“纸上得来终觉浅,绝知此事要躬行”。
以上是一般原则,是泛化。下面结合具体例子,讲一个特化。
学围棋的人,很关心怎样提高棋力的问题。我也请教过多人,总结下来有这样一些方法:
- 做死活题。吴清源说:围棋最重要的是死活。然而下棋的目标却不是杀死对方的棋或自己做活。死活只是手段,赢棋是目标。不过你若“不知死活”,目标当然遥不可及。看高手下棋时,我觉得必死的棋,忽又活了;我觉得是一堵大厚势,不可能出事的棋,忽又变成了孤棋,最后大龙愤死。不由感叹:满盘尽是死活题。做死活题时,是学习,是思考,也是实践。
- 打谱。在打谱中,你可以看到中国古棋凌厉的攻杀,秀策那看似平淡无奇却闪耀着万丈光芒的小尖,吴清源和木谷实对布局的新理解,武宫正树天马行空的宇宙流。打谱时,是学习和思考。再在实战中用出来,就是实践了。
- 实战。没有实战不行的高手。高手是通过无数实战而长成的。实战有输有赢,但关键是要通过复盘,分析输赢的原因,从而提高棋艺。
- 复盘。当你水平不行时,最好是请高水平的老师复盘,这样进步快。当你到达一定水平时,可以自己复盘或与对手一起复盘,摆出一些变化图,分析实战中决策的合理性。反思是最重要的一项实践。所有实践一律平等,但有些实践更平等。
2009年3月26日星期四
《持续集成》的译者序
软件项目开发有两大难题:一是确定软件的需求,即确定目标;二是确定目前离目标还有多远,即确定剩余的工作量。第二个问题就是项目缺少可见性的问题,这对“人月神话”做出了“巨大贡献”。当一个项目经理或一名开发者说已经完成了80%的任务,您必须保持审慎的乐观。因为剩下的20%可能还需要80%的时间,甚至永远也不能完成。您可能迟迟不能拿到可以部署的软件,对此所有的人都无能为力,只能表示深深的遗憾。这确实让专业软件开发者的声誉蒙羞。但是对于大型软件开发这样的复杂工作,我们的经验确实显得有些不够。
《持续集成》这本书向我们介绍了一种增加项目可见性、降低项目失败风险的有效实践。许多软件开发的资深人士认定,这种方法非常不错。我们不必把宝全部押在最后那一次“大爆炸”式的集成上,而是采用“早集成、常集成”的策略。这样做可以减少缺陷引入和缺陷发现之间的时间,提高开发效率,降低风险。您对项目完成百分比的报告将有更大的信心,而且任何时候,您都可以得到一个可以部署的软件。虽然功能可能还没有全部实现,但它是可用的!
这本书向我们揭示了这样一个道理:如果一件事很难,而您又必须做,不妨经常去做,每次做一点点。其实这也是古老的“分而治之”思想的一种应用。滴水穿石,跬步千里。
敏捷软件开发的许多实践都是互相关联的。持续集成在与其他实践结合时,才能将它的效用发挥到极致。这本书除了介绍CI的基本原则和工具之外,也介绍了如何将测试驱动、代码审查、数据库集成、信息反馈等实践和工具。人(思想)、过程和自动化工具的完美结合,将形成一个和谐的开发生态环境。如果您一直在追求效率更高的软件项目管理方法,我相信这本书一定能给你带来一些启发和灵感。
一本好书使您改变。它将改变您的思想,您看待问题的角度和方式,最终,它将改变您的行为。然而,所有具有重要意义的改变都不会在一夜之间发生。改变随时都在发生,但按照您的意志去领导变革却很难。如果您相信这种变革必须发生,不妨朝着这个方向去努力,经常改变,每次改变一点点。
软件业中没有银弹,不可能有某种东西在短时间内让您的开发效率提高10倍。但是我们也很容易发现不同个人和不同团队之间的开发效率相差巨大,不止10倍。那些软件高手和明星团队就像职业围棋选手,他们高得惊人的效率是多年用心改进实践的结果。
在这本书的翻译过程中,我学到了很多,因此郑重地向大家推荐它。如果这本书对于您改进软件开发实践有所帮助,我将十分高兴。
2009年3月20日星期五
Socket编程和协议设计
基于TCP协议应用协议有一个好处,可以用telnet来调试。于是我祭起Putty,发起请求,这下看到了预期的应答数据。
我的程序错在哪里?错在readLine(),它要等待一个'\n'。而按照应用协议的规定,不会在一次应答结束时返回'\n'。所以readLine阻塞了。
正确的写法应该是怎样的?只从socket读取一次数据是不对的。由于网络延时是不确定的,所以读一次数据不知道是否读到了完整的应答。一定要根据应用协议的规定,读到结束字段。而这个应用协议规定了用''分隔字段。于是我必须根据应答字段的个数进行读取,直到读完一条完整应答为止。
事情还没完。根据请求是否成功,服务器应答信息的字段数是不一样的。所以应答读取的程序应该先读到成功失败标识,再判断成功失败,读取后面的应答字段。
这导致了协议对话部分的程序相当复杂,而这种复杂本来是可以避免的。为什么会出现这样的结果?
原来的协议还附了一段C程序例子。正是这段例子,让我对问题的原因有了一个大概的猜测。
这段C程序初始化了一个缓冲区,并往里面填充了0。然后从socket中进行了一次读取,放在缓冲区中。不幸的是,这段程序是错误的。因为网络延时不确定,所以读取的数据可能是不完整的应答。写例子的人就当它是完整应答了!这样写是简单,但是却是错的。
我想,如果设计协议和写C例子的人(可能是一个人)如果概念清楚,还是会把协议设计成以'\n'来结束一次应答吧。
2009年3月19日星期四
FDD伸缩性和实现客户价值的TOC理论
前两天,我刚好看到James Shore的一份Presentation,其中提到增加客户价值的5种方式,真是敏捷方法学的强人,抓住了客户价值这一点不放。
提到这份Presentation的原因是,他的5种方式中的第一种要求一个团队同时只做一个项目。他的图示说明非常具有说服力,一下子就让我完全同意。但接下来问题出现了,如果是一个千人的公司,同时也只做一个项目?这是不是有点荒唐?他自己也意识到了这一点。
关键之处仍是大部分敏捷方法学的软肋:可伸缩性不好。
FDD声称可以适应很大的项目,于是我们可以设想这样的场景:一个50人15个月规模的项目(新加坡项目的规模),如果给我750人,那么1个月就搞定了!这将为客户提前14个月获得投资回报。如果真能实现这一点,我想世界会震惊的。
干一个月的活,收钱。这是我喜欢的方式。干一天的活,收钱,这是我爱的方式。
好吧,我承认,我身体里理想主义的部分和浪漫主义诗人气质在发挥作用了。事实上,这样的事情还没有发生。存在就是合理。合理之处在哪里?有什么理论支持?
实际上,IT系统为业务带来价值不是只有开发这一个环节。存在着一个“开发-实施-反馈-接受”的循环。假设750个人一个月开发了1500个特征,这将在业务部门带来巨大的冲击。只要想想ERP的实施就知道了。按照TOC的理论,实施、反馈、接受的环节将成为实现客户价值流水线上的新瓶颈。
按照精益制造的思想,一个环节的产能超出系统其他部分的产能太多,绝对是坏事而不是好事。我们应该根据系统的总吞吐量,不断发现新的瓶颈,提升瓶颈的产能。
不管怎么说,这样想想还是挺妙的。现在瓶颈不在我负责的这一块了。也许在不久的将来,企业能够接受一下实施1500个新特征的冲击。
2009年3月17日星期二
Mediator模式
一个朋友问起Mediator模式,为什么类图中具体类之间存在着依赖关系?这样耦合是不是太厉害?是不是“Gang of Four”当年不太严谨所致?
这要从Mediator模式要解决的问题说起。Mediator模式是为了解决对象间多对多的通信协议问题。碰巧的是,我在《基于组件开发》一书中也读到了相关的内容。
如果A组中的M个对象与B组中的N个对象相互之间都需要通信,那么在未使用Mediator之前,它们之间需要M*N个通信协议。这在对象很多的情况下很麻烦,在添加一个对象时要做的工作也很多。
“所有问题,都可以通过添加一个间接的中间层来解决”。所以我们引入Mediator作为中间层,它的工作就是进行协议翻译。设计一个中间表示模式,实现所有对象通信协议到中间表示模式的翻译。这样,原来的M*N个通信协议就变成了M+N个通信协议。在添加一个新的通信对象时,也只要增加一种互译协议。
回到朋友的问题。根据这样的设计,Mediator必须知道如何完成这种协议翻译。因此,它必须了解具体参与通信对象的协议实现方式。所以,Mediator的实现就依赖于通信对象的实现了。
CORBA就是这样的一个例子。当然,我们可以把协议翻译这一块拿出来,做成一个协议适配器,通过再添加一个中间层,消除Mediator的具体实现与通信对象的具体实现之间的耦合,这是故事的续集。
我们还可以让通信对象各自负责实现协议翻译。毕竟,架构就是系统由哪些组件构成以及这些组件之间的关系。猜猜我们得到了什么?MQ(消息队列)/MOM(基于消息的中间件)/ESB(企业服务总线)!
2009年3月4日星期三
免费的UML建模工具
StarUML。画出的图不够漂亮。似乎是韩国人用Delphi写的。据说停止开发很久了。
Netbeans的UML模块。6.5以后的免费UML模块由Visual Paradigm接手继续做了,易用性方面有明显改进,图也一如既往地漂亮。这是我主要的画图工具。
Eclipse插件eUML2 。没有使用过,看截屏似乎还比较漂亮。
Eclispes插件 MDT-UML2。没有使用过,网页上没找到截屏,看EclipseCon 2008的一些Slide似乎还比较漂亮。
UML图和代码的正、逆向工程是很有用的特征,但敢于展示这项强大特征的工具似乎不多。如何维护UML模型到代码的可追踪性,是项目团队要认真考虑的一个问题。
2009年3月1日星期日
Amdahl's Law与伸缩性(scalability)
Amdahl's Law指出,如果您的系统中的任一部分要求串行化处理,而不能并行化,那么系统就会撞上伸缩性之墙,不能再提升性能,不论您再投入多少硬件。
关于Amdahl's Law的细节,请参考Wikipedia的条目。
伸缩性是架构设计中考虑的一个重要方面。基本上,我们是在讨论如何提高系统的吞吐量。这让我想到《目标》一书中的约束理论(TOC),扩大系统吞吐量方法是找到瓶颈并消除。书中有一个例子,新购买的一台强大的加工机器取代了原来的两台旧机器,但不幸却成为了系统的瓶颈,因为大多数零件需要“串行地”经过这台机器的加工。解决办法?找回原来的两台旧机器,让工作能够“并行的”进行!
Prevayler在主页上声称比MySQL快3000倍,比Oracle快9000倍。但不管怎样,它也会撞上伸缩性之墙,因为它的事务是“串行”处理的。硬盘的访问也是串行的。这是否意味着我们无法设计出高伸缩性的系统?
不是的。我们可以通过硬盘阵列,数据库表分区(Sharding),让硬盘访问能够并行地进行。我们也可以用Prevayler实现LDAP的后端,利用LDAP协议本身的复制和分子树的能力,化解串行访问对伸缩性的影响,构造高伸缩性的系统。(做一个支持大量并发的身份证查询系统?)
开发方法学也有伸缩性问题。我们曾听说过,许多敏捷方法“don't scale very well”。不能支持大型团队,在短时期内交付大量的特征(客户价值)。究其原因,很大程度上是因为“隐式的知识”沟通是串行式进行的。
传统开发方法学也存在沟通问题。团队加入新成员后,短期会出现产能下降。因为新成员需要学习,老成员要抽出时间来教他们。
提高产能的方法就是把串行消除掉,让项目能够并行地开发。这一点恰恰是特征驱动开发(FDD)做得比较好的地方。前期经过尽可能短的串行处理(整体建模、特征列表、优先级)之后,后续的开发就可以并行展开了。所以搞FDD的人声称,可以实现可重复的成功,可以适应大型的开发项目。新加坡项目就是50个人15个月的银行项目。
FDD是一系列最佳实践的组合。并行开发的另一个关键在于组件化。使用同样的组件化架构风格,处理子系统、系统、和“众系统之系统”。让开发者熟悉了系统的一个部分,就很容易熟悉系统的另一个部分。这就是Fred Brooks所谓的“概念完整性”。将不同粒度的业务事件,开发成不同粒度的组件,用Peter Coad的话来说,实现了“模型的伸缩性”。
开发完了再测试(瀑布式)还是开发一点测试一点(迭代增量式)?Amdahl's Law告诉我们,要并发!得到详细的需求规格说明书后再开始设计和编码,还是像FDD那样把局部的详细需求放到迭代开发之中去?Amdahl's Law告诉我们,要并发!各模块开发好了之后再集成,还是持续集成?Amdahl's Law告诉我们,要并发!
由此我甚至进一步想到,FDD的前面三项串行工作也不是必须的。在实践中也遇到了这样的情形,特别是在创新的业务领域中。新加坡项目刚好是成熟的业务领域。我们可以让业务模型一点点长出来。清楚一部分,做一部分,但要在同一种架构风格(如彩色UML或5w and an H)指导下,确保整个系统的概念完整性。
和这些前辈高人神交,人生一大快事也!
我想讲的课
1. 软件需求过程
软件开发中最难的部分是确定需求。需求是一项社会工程。
2. 软件测试与自动化
按传统软件工程理论,测试占到开发工作量的50%。
3. 彩色UML建模与特征驱动开发
哪一种建模方法让许多接触它的人觉得“一旦拥有,别无所求”?
4. 持续集成
日构建是微软最值得称道的开发实践之一。持续集成让客户、
5. SOA
个人、团体和大型企业都在向其客户提供服务,
6. 敏捷开发方法学
敏捷开发方法学是业界顶尖思想家赠予我们的财富。
7. 过程改进与CMMI
如果你只想学一个日语单词,那我推荐“Kaizan”,
8. UML建模
做软件就像开车,你不能设定好一个方向就一直往前冲。
9. 软件配置管理
版本控制/配置管理是绝大多数开发项目都会执行的一项日常实践,
10. 软件复用
复用是实现令人满意的效率和品质的唯一途径。
2009年2月26日星期四
转账Transaction(交易/事务):业务层和持久层设计
巧的是,在Prevayler的实现中,强调的也是事务:它要求开发者设计可以序列化的“事务对象”,利用Java的序列化机制实现持久,而所有对象的目前状态则保持在内存中。
三年以前我开始看Prevayler时,觉得它的这种“事务对象”不好理解,别扭,不是我喜欢的东西。在读了彩色UML建模之后,我理解了这种持久设计。下面以银行账户转账的例子来说明我的理解(注意,这个例子是虚构的,不存在于任何真实的系统中。如有雷同,纯属巧合):
业务模型
关于这个业务模型,有两点说明:
- 按照“5Ws and an H”的方式设计。MoneyTransferring本身记录了转账的金额和时间,说明了这是个什么事(What),何时发生(When)。MeansDesc是转账方式描述,可能是通过ATM机、柜台、电话银行等方式完成这次转账,说明了发生方式(How)。Terminal说明这次交易在哪里发生(Where)。Staff和Customer说明了参与这次转账的人(Who)。转账的理由(Why)则因为没有很合适的分类方法,可能作为MoneyTransferring的一个字符串属性。
- 所有的类,都可以作为分类统计的依据。例如,我们可以根据一个账户的MoneyTransferring事务和存入、取出事务,确定这个账户的活跃程度。或根据MeansDesc来确定用户对转账方式的偏好。“5Ws and an H”都可以在未来成为查询和统计的条件。
持久设计
持久层设计为一个接口,其中的一个方法实现转账事务:
PersistentLayer.transferMoney(MoneyTransfering mt) ;
如果以关系数据库的方式来实现持久,这个事务将包含3条SQL语句:一条插入(INSERT)和两条更新(UPDATE)。更新必然是用原来的账户余额加上或减去转账的金额。数据库事务将保证这个操作的ACID。如果转出账户余额不足,且数据库设置了约束,余额字段不能为负,这个事务将失败。
但是,我们在开发过程中还可以临时使用内存的持久方式,实现一个“不持久的”持久层。这个持久层实现只是简单地将MoneyTransfering对象插入到一个全局的List中,并假定业务层已经将相应的两个账户做了改动。
这两种实现在对业务层的假定方面有一个细微的区别。第一种SQL实现只关注传入的MoneyTransfering对象拖着的两个Account对象的ID,而不关心它们的余额。第二种内存实现则假定业务层已经处理好了它们的余额。如果我们只用内存实现来做一些不严肃的测试,这样也未偿不可,但严格来说,我们对持久层接口的契约理解上,出现了偏差。
所以,正确的做法是,业务层不负责改动账户余额,全部交由持久层实现来完成。这样内存持久实现将做3件事,一次插入和两次更新。这下和数据库持久的语义一致了。
之所以费这么大劲讨论内存持久对PersistentLayer接口语义的支持,是因为有Prevayler的存在。它把“内存+对象序列化”做为一种正式的持久方式,用于生产系统中。如果我们写一个PersistentLayerPrevaylerImpl,那么这个实现对这个事务的处理是:将MoneyTransfering对象插入全局的List,更新两个账户余额,将MoneyTransfering对象通过序列化机制持久到硬盘。内存里保存的是最新的数据快照,持久存储上存放了所有数据变动的历史。这和HSQL是一样的道理。
您可能听说过DB4O,或者听说过BerkeleyDB,还可能听说过Tokyo Cabinet。在云计算时代,我们有很多的持久机制可以选择。为什么不把业务代码设计得灵活一些,可以适应不同的持久机制呢?多维护一个PersistentLayer接口的开销并不算大。
小结
- 我们用“5Ws and an H”的方式对业务事件建模。
- “5Ws and an H”成为业务数据查询和统计的条件。
- 通过PersistentLayer接口来统一持久层语义。
- 根据不同目的和环境,采用不同的持久层实现。
- 云计算时代的持久不同于C/S时代的持久。
2009年2月17日星期二
Peter Coad的7项修炼
Peter Coad是我喜欢的一位面向对象专家和软件创业者。他在上世纪九十年代与人合著了6本关于面向对象软件的分析、设计和编程的书籍,以《面向对象分析》一书中和Yourdon共创的Coad/Yourdon方法而成名。1999年,他创建了TogetherSoft公司。2003年,TogetherSoft卖给了Borland公司,他成为了Borland公司的副总裁。后来,他离开了Borland,兴趣转向了软件之外,教人读希腊文版的圣经。
他最值得一提的技术贡献,就是提出了“彩色UML”建模方法,这也是他最后一本书《彩色UML建模》的主题。在这种建模方法中,他使用了4种颜色,代表4种不同设计意图的类,创造性地回答了面向对象分析和设计的一个根本问题:“怎样识别类,并确定方法和属性?”这本书已由机械工业出版社引进版权,出了中文版。
我对Coad的关注始于十多年前,那时朋友向我推荐了北大出版社引进的《面向对象分析》一书。后来因为学习Java、UML建模、关注Borland公司的产品,又看到了TogetherSoft和Coad的名字。再后来,敏捷方法学兴起,我又看到了Coad,他是特征驱动开发(FDD)的主要支持者。向师傅学习,要学师傅练功的方法,而不只是师傅的招式。我想知道的是:为什么Peter Coad会发明彩色UML的方法?或者说,Peter Coad是怎样炼成的?
在他1994年左右的一篇文章中,对这个问题给出了一些提示。这篇文章标题是“Amplified Learning”,介绍了“7种智能”的理论和应用。
语言是第一种智能。著名的通天塔的故事体现了自然语言表达的重要性:失去了语言沟通能力,团队协作就变成了不可能的任务。语言能力的习得,有一句话可以概括:“听说领先,读写跟上”。学习本国语言和外语都是如此。语言的重要性,实际上体现的是沟通的重要性。据说,当年TogetherSoft公司在面试时,会先让被面试的人选一个与软件开发无关的话题讲15分钟。通过这样的面试环节,可以了解面试者对生活的一些理解和表达沟通的能力。这里我还想强调一下中国程序员的英语能力。由于软件方面的最好的信息几乎都是英语的,所以良好的英语能力对程序员有很大的帮助。一般来说,在各行各业,最成功的人都是拥有最好信息的人。
代码的可读性也是语言能力的体现。Knuth在1984年提出的Literate Programming,把编程比喻为写文学作品。既然是文学作品,就会有不同的风格。有晦涩难懂,也有深入浅出、生动有趣。所以作为一名好的程序员,需要对自然语言和编程语言都有深刻的理解,要懂得欣赏和创造文章之美,代码之美。
那么什么样的文章是好文章?一个留美回来的英语作文教师曾对我说,好文章首先要有“something new(新东西)”。新的观点、新的解释、新的视角、新的实践、新的证据......都可以。这是西方鼓励创新的思路,所以我们会看到软件业的创新层出不穷。中国人对好文章的评判标准更注重传统。《文心雕龙》里提出的标准是“本乎道,师乎圣,体乎经,酌乎纬,变乎骚”。把软件提升到道的高度,那就是C++创始人Bjarne Stroustrup所说的,“人类文明运行于软件之上”。
数理逻辑是第二种智能。我们需要对数字敏感,需要具有归纳和演绎的能力。这样的能力虽然也包含在语言学习之中,但更多地包含在数学、物理等理科课程中。在西方,学理科的学生几乎都读过Pólya的《怎样解题》一书。数学是思维的体操,它让我们的思维更灵活、更缜密。懂得欣赏数学之美的人,会看到简洁的公式和逻辑的力量。许多大学的计算机系都是从数学系分出来的,一些老师以前是学数学或物理的。史书记载:“尧造围棋,教子丹朱”。学围棋也是对逻辑思维很好的锻炼。和数学一样,从围棋中我们也可以看到,从一些简单的规则出发,可以怎样建造一个瑰丽的宫殿群。另外,在软件复杂度测量、软件工程管理方面,也需要许多量化的测量指标。
以上两种智能可能是学校教育中教授得最多的内容,体现在语、数、外三门主课上。能够熟练灵活地运用语言进行沟通,并能够运用逻辑推理来找出问题和解决问题,我们就具备了一些基本的能力。但除此之外,还有一些扩展性的智能,对于我们软件开发者也很重要。
空间色彩是第三种智能。九十年代是GUI的十年,在这十年里,软件的美由内而外,扩展到了视觉领域。叔本华说,“美是最高级的善,创造美是最高级的乐趣”。但是,我们不时会看到一些殊无美感的设计,那真是叫“乏善可陈”。Apple公司的操作系统中,利用色彩来表示窗口的控制按钮,给人以艺术的享受。Peter Coad的彩色UML建模方法利用4种色彩来表示4种不同的架构型,通过色彩强化了信息沟通的效果。我还记得当初在Borland公司的集成环境中第一次看到彩色语法制导时,带来的那种震撼。作为程序员,我们经常需要阅读图表、画设计草图、运用图表或幻灯片来传达信息,空间色彩能力也是相当重要的。
“流光容易把人抛。红了樱桃,绿了芭蕉。”“知否?知否?应是绿肥红瘦。”中国人讲究“诗中有画,画中有诗”。软件是散文,是诗,其中也蕴含着色彩和空间之美。
音乐是第四种智能。科学家有音乐特长的例子比比皆是,并且他们都认为,音乐对他们的专业是有启发的。Spring框架的作者之一Rod Johnson拥有音乐博士学位。旋律和节奏是音乐训练的两个主要方面。很多歌曲要记住歌词不容易,但旋律却让人记忆深刻。我喜欢在工作环境中使用背景音乐,这样能舒缓压力,激发创造力。音乐反映了我们对世界的一种认知模式,反映了道。《庄子·养生主》载,“庖丁为文惠君解牛,手之所触,肩之所倚,足之所履,膝之所踦,砉然响然,奏刀騞然,莫不中音,合于桑林之舞,乃中经首之会。”庖丁是个解牛的专家,是个哲学家,也是个音乐家。
运动锻炼是第五种智能。许多职业棋手都通过运动锻炼,保持有活力的工作和思考,使自己处于最佳状态。极限编程(XP)在一开始提出“每周工作四十小时”作为一种最佳实践,但是如果你的身体有问题,就会连四十小时都做不到。后来XP将这一实践改成了“保持有活力的工作”,我想大概是因为有些身体好的人,每周工作六十小时也一样很有效率吧。要养成锻炼的习惯,争取做到“每天锻炼一小时,健康工作五十年,幸福生活一辈子”。
以上三种智能,体现在音、体、美这三门副课上。 将这三种智能提升到一定水平,就可以称得上全面发展了。
自知是第六种智能。曾子曰:吾日三省吾身。苏格拉底说:“认识你自己”。蘧伯玉年五十而知四十九年非。正确地认识和评价自己,不断地反省自己,这是进步的基础。《从优秀到卓越》一书的作者Collins说,你应该问自己一个问题:在哪个领域你可以做到世界前三?人贵有自知之明。
知人是第七种智能。越来越多的工作,正由团队来承担。有一本书名叫“Teamwork Is an Individual Skill”,书名就很有启发:团队合作是一项个人技能。要成为团队的一员,你需要读懂他人,并具备一定的社交技能。然后,通过实现团队的目标来实现自我。“己欲立而立人,己欲达而达人。”如果你是团队的领导,那么知人善任就是一项重要的能力。刘邦正因为用对了张良、韩信、萧何而得到了天下。
寻找怎样的人作为合作的伙伴或朋友?益者三友:友直、友谅、友多闻。怎样看人才不会看走眼?子曰:视其所以,观其所由,察其所安;人焉廋哉。人焉廋哉。
以人为镜,可以明得失。见贤思齐焉,见不贤者自省也。所以,知人也可以促进自知。
知人者智,自知者明。以上两种智能,就属于情商的范畴了。(透露一个秘密:一下记住7件事是有难度的,所以我把它分成3个部分——主课、副课、情商。你能记住吗?)
每个人在这7种智能上的发展都不尽相同。可能因为各自的成长经历和条件,我们在某些方面的智能没有得到很好的发展。随着年龄的增长和自省智能的提升,你可能会认识到人生就是练功、升级。如果你现在已经走上了一条主动的持续改进之路,那么你打算如何在这些方面提高自己呢?
2009年2月10日星期二
‘5Ws and an H’流水账和组件
流水账
- When:这个业务事件何时发生?
- Who:谁参与这个业务事件?
- What:这是个什么事?涉及什么物品?
- Where:这个业务事件发生在哪里?
- Why:为什么会发生这个业务事件?
- How:这个业务事件以何种方式发生?有无细节?
让我们来看一个例子:
特征:为客户打印发票(收据)
- When:记录下“打印发票(收据)”这个业务事件发生的时间
- Who:参与这个业务事件的人有两个:客户和业务员
- What:这个业务事件是“打印发票(收据)”,涉及到一张纸质发票(收据),上面有序列号
- Where:这个业务事件发生在公司旗下众多连锁店的一个里
- Why:因为客户付了一笔款,客户付款是前驱业务事件
- How:用户付款时享受了折扣优惠,需要在发票(收据)上注明
当我们把一个业务事件的这些方面都记录下来时,信息就完备了。值得一提的是,在具体业务事件中,上面的每个因素都可以有多个具体的值。例如,在持续集成中,因为前面有3次提交,然后在5分钟内没有新的提交,这触发了一次持续集成。那么,这次持续集成事件的前驱业务事件就有3个。
组件
这个业务事件构成了一个组件,它对使用的上下文做了一些假设。如果这些假设没有变化,这个组件就不需要改变,可以复用。
也可以把这个组件看成一个独立的数据源,它提供对这个数据源的数据分析统计功能。例如:
- 这个月哪个业务员开出的发票最多?
- 从上次领发票本到现在,某个连锁店开出了多少发票(是否要提醒领新的发票本)?
组件粒度
组件粒度也就是业务事件的粒度。“进行一次销售”这个业务事件可能由“下订单”、“付款”、“打印发票”这3个小粒度的业务事件组成。 于是,我们可以在“进行一次销售”这个业务事件的“How”部分,标明它由3个明细业务事件构成。如果我们还允许一周内无条件退货,那么还可以包含“退货”的业务事件。
小粒度的组件组合起来,就构成了大粒度的组件,系统以这种方式体现伸缩性。这样做的好处在于:
- 简单一致地伸缩,设计小系统和设计大系统的原则一样
- 在不同的抽象层面上看系统,如操作层面和管理层面
总账会计师和CIO
总账会计师要能够从企业的总账中分析出企业的盈利能力和风险。CIO要能够从业务事件流水账中按“科目”进行整理,通过分析找到实现企业目标的风险和机会。
2009年2月8日星期日
RESTful的停车场图和开发Dashboard
- 以资源为中心的方式。
- 所有相关的资源都可以通过统一资源标识符(URI)来寻址。
- 统一通过HTTP来访问:GET、POST、PUT和DELETE。
- 内容类型协商允许从同一个URI取得不同表示形式。
- REST风格的服务很容易通过运行在web浏览器、其他客户端或服务器上的代码来访问,这在Ajax的背景下很流行。
- 完全利用WWW的缓存机制
- 为同一资源的多种表示层提供服务。
以上内容引自正在译的新书“Executing SOA”,这启发我用REST的观点来看问题。REST不是新观点了,我现在才有点理解,Shame on me!我的博客中“最近的访客”就是一个极好的REST例子。
如果用REST的观点来看,项目的进度是一项资源。特征驱动开发(FDD)的停车场图就可以作为一个很好的例子。
- GET:取得停车场图(图或数据)
- POST/PUT:创建或更新这个停车场图所代表的资源
- DELETE:删除这个停车场图所代表的资源
然后,我们就可以把这个停车场图无缝集成到别的网站中。集成到哪里呢?项目开发Dashboard!类似像Sonar或maven生成的项目网站这样的。
更进一步,如果所有的资源(版本控制、持续集成......)都放在云端,然后用RESTful的方式发布出来,我们就可以随便用一个内容管理系统(CMS),定制自己的项目Dashboard,最简单的可能就是使用一个wiki。
云时代真的来了。很想写一个停车场图的RESTful Web Service。
2009年2月7日星期六
测试计划
最根本的一条原则:测试工作和开发工作结合得越紧密,效果就越好。
理想中的测试工作安排是这样的:
- 在项目早期,测试团队的骨干参与需求工作,确保需求的可测试性,同时也帮助消除了需求的二义性。
- 在概要设计/架构设计阶段,测试团队同步进行测试概要设计,确定测试的基础架构和基础设施。
- 在详细设计/迭代开发阶段,测试团队同步进行测试用例设计和开发,确保开发团队通过持续集成交付的每个feature都在最快的时间内得到测试。容量测试、压力测试等也要在条件一成熟,就马上进行。
随着产品代码的不断增加,自动化测试的代码也不断增加。这些测试代码在整个软件生命周期中都有用。
测试成本低的的设计是好设计,测试成本高的设计是不好的设计。降低测试成本是降低软件总体拥有成本的根本途径。
人总是会犯错误。尽早发现,尽早更正,确保决不再重犯同样的错误,是我们的追求。
参考书籍:《快速测试》、《持续集成》、《Java测试新技术:TestNG和高级概念》
2009年1月26日星期一
彩色UML建模、SOA和《目标》
每一个粉红色的MI及其相关的黄色角色、绿色参与方-地点-物品和蓝色描述构成了业务流程中的一个步骤。根据彩色建模的伸缩性设计,这个步骤的粒度可大可小。可以是大粒度的“完成一次销售”,也可以是小粒度的“开发票”或“收款”。
SOA的思想是要把事务组件web service化,再用BPEL来组织流程,以这种方式来组织业务逻辑。
另外,在数据逻辑或信息逻辑的方面,SOA会把一些数据分析和挖掘的方法也设计成可以复用的web service。
换句话说,我们先对业务过程记上流水账,然后再对流水账进行分析。我们记录下When/Who/What/Where/Why and How(‘5Ws and an H’ ),然后就可以进行各种分析了。你可以在数据库中只记录下最小粒度的业务事件,然后以虚拟数据源或信息服务的方式,提供大粒度的业务事件的相关信息。
为了让流程能够灵活变化,我们可以考虑消除步骤之间的耦合,即在红色的MI之间不建立直接关联,而是利用它们共同的关注的内容,如“订单”,来组织流程。如果我们要“评估销售的及时性”,只要得到“订单”下单的时间和付款的时间之差。设想有一个信息服务,作为一个虚拟数据源,提供一段时间内所有订单的完成时间和各步骤完成的时间。
Goldratt的《目标》是所有管理者都应该读的一本书。其中提到了一个重要观点:所有在原材料方面的投资,如果还没有变成有效产出(产品卖出去并收回货款),都应该看成是负债。这种负债乘以天数,是一个重要的测量指标。我们应该努力减小它。根据这一观点,制造业的企业提出了许多解决方案,如按订单生产、精益制造等等。而制造业的人们学到的东西,看来IT业都要重新学习。
回到我们的主题,这和彩色UML或SOA有什么关系?
关系就在于:我们的IT系统,要能够帮助企业的决策者找到系统瓶颈。看过《目标》的人应该记得,那里面有个神奇的CIO,每次都能告诉厂长系统的新瓶颈出现在哪里。然后大家不断优化瓶颈,使系统的产能和效率不断提高。
如果您就是那位CIO,请你告诉我,我们的订单执行过程中,哪(几)个环节耗时最多?
2009年1月15日星期四
设计决定、反悔、霰弹式修改和架构污染
(老文章,曾在《程序员》杂志发表。用了vim得到彩色的代码,但可惜在《程序员》上没有印出彩色。)
设计决定
“好吧,在这一个项目中我们就使用Google Guice!”我们坚毅、果敢、英明神武的首席架构师宣布了这个决定。
为什么不呢?依赖注入是这么成功的一个概念,我们使用Spring作为依赖注入框架已经好多年了。而Google Guice又给我们带来了许多的改进。
首先,Guice抛弃了XML配置文件,这使它具备了“重构友好”的特征。在Guice中,我们通过Java代码来说明接口(服务)和实现类的绑定,例如下面这一段代码:
public class MyModule extends AbstractModule { |
这样,当我们需要重构(修改)接口或实现类的名称时,就可以利用集成开发环境提供的重构功能来完成,不需要再手工修改XML中的类名。在重构深入人心的今天,我们不希望改一个类名还需要额外的工作。
第二,在抛弃了XML配置文件之后,应用程序就不再需要一个XML解析器了。这样,Guice就比Spring“瘦身”了不少。高手们总是对减少资源的占用怀有特殊的爱好,不是吗?Guice 1.0的Jar包只有544K(高手可能会认为还是大了一点),不依赖于其他的Jar包。另外,由于不需要读取并解析XML配置文件,执行的速度也提高了不少。
第三,Guice使用了JDK 5的新功能,其中一项就是泛型。我们不再像以前使用Spring那样进行强制类型转换了,我们有了编译时刻的类型安全检查,而且代码也简化了。我们像下面这段代码那样取得组件实例:
public class MyApplication { |
第四,Guice使用了JDK 5的另一项新功能,annotation!这个词太可爱了,听起来和innovation有点相似,以至于我们一时间不知道如何将它翻译成中文。Guice提供了近乎神奇的@Inject annotation,满足您各式各样的注入需要!(我相信这些注入方式都是在Martin Fowler的那篇著名文章中提到的。)
我们可以实现构造方法中的注入,像下面这样:
public class Client { |
也可以对属性直接注入,甚至不需要设值方法,像下面这样:
public class ServiceImpl implements Service { |
第五,Guice有在真实应用中成功使用的历史。据说,它曾在Google的广告应用程序中使用,而Google的广告应用程序可能是目前世界上规模最大的应用程序!
最后,更重要的是,品牌就是品质。Google是如此受人尊敬的公司,吸引了许多重量级的人物加盟,已经成为许多IT人士向往的地方。使用Google的荣誉产品,就像使用JDK自带的包那样可靠,我们还有什么可担心的?据说Spring的新版本也向Guice学习,使用了annotation。模仿就是一种肯定,不是吗?
反悔
“新郎,你愿意娶新娘为妻吗?”
“是的,我愿意。”
“无论她将来是富有还是贫穷、或无论她将来身体健康或不适,你都愿意和她永远在一起吗?”
“是的,我愿意。”
“将来看到更漂亮、更好、更合适的,你也不会改变今天这个决定吗?”
如果理性地回答,答案就太不浪漫了。因为未来不可预见的事情实在太多。好在热恋中的人们倾向于不那么理性,所以答案是“永远。如果非要加上一个期限,我希望是一万年”。
我们的架构师正在与Guice开始一段新的感情。这种感觉太好了,上次有这种感觉好像还是在刚使用Spring的时候,那已经是几年前的事情了。现在他压根没想到以后可能会像抛弃Spring一样抛弃Guice。
直到有一天,一个刚刚参加工作的小兄弟问了他一个很傻的编译错误。他告诉这个小兄弟,要使用@Inject,必须这样:
import com.google.inject.Inject; |
小兄弟满意地继续他的开发,但我们的架构师却觉得有一丝不祥的感觉掠过,空气中似乎飘荡着一种坏味道。
他检查了一下项目源代码。在第一次迭代中,我们写了50多个类,其中大约有一半都包含了@Inject!这种情况不妙,简直太不妙了。这违反了他多年的经验所建立起来的原则信仰。
这违反了Larry Constantine的“高内聚、低耦合”的原则。这条伟大的原则如此简单而深奥,以至于不断受到人们的践踏。
虽然“疯狂的Bob Lee”为Guice设计出了极小的API接口,但在使用@Inject这一点上,却让人不太赞同。他似乎鼓励我们在自己代码的各处插入@Inject。
当我们把一个设计决定分散到代码的不同地方,它就会成为难以改变的既成事实。即使是对于JDK自带的类,我们也不能这么做。我们不能在所有的业务类中都引用JDBC的包,使用Connection、Statement和RowSet。相反,我们应该把它们放在持久层中。也许,我们应该使用Hibernate这样的框架,将它们封装起来。也许我们还需要再封装一层,设计一个PersistentLayer接口和一个PersistentLayerHibernateImpl实现。这样,如果我们以后有新想法,只要再写个PersistentLayerJdbcImpl或PersistentLayerDb4oImpl或PersistentLayerIbatisImpl或PersistentLayerHibernateAndIbatisMixImpl。
如果我们把使用Guice这个设计决定作为一种默认的假定,让它散布在代码的各个角落,那么将来我们要反悔的代价就非常大。实际上,我们就不能反悔了。
我们会反悔吗?我们需要反悔吗?不能反悔的决定是我们可以承受的吗?
我们的架构师做出了一个痛苦的决定:我们需要能反悔。毕竟,作为一名架构师,他的全部声誉就在于设计出有弹性的架构,可以容纳将来可能的变化。
霰弹式修改
我们的架构师是经验丰富的。办法总比问题多,不是吗?关键是要发现问题。根据“所有的问题都可以通过添加一个间接的中间层来解决”这一原理,他设计了一个Factory,这是一个全局的Singleton:
public class Factory { |
然后,在每个用到服务依赖注入的类中,在构造方法里加上赋值语句:
public class Client { |
注意,我们没有为service属性加上final关键词,这样您仍可以有setService()方法,可以利用Mock对象对Client类进行单元测试。
剩下的工作就是从项目已有的代码中去掉所有的@Inject。这是一种“霰弹式修改”,不幸中的万幸,我们只要修改20多个类,而不是200多个、2000多个。
由于有自动化的测试类作为保障,这种改动很快就完成了。如果以后我们想换一个依赖注入框架,就不需要“霰弹式修改”了。我们甚至可以很容易改回用Spring,当然,目前还没有这个必要。
再见了,@Inject!
架构污染
不久后的一天,我们的架构师在浏览代码时,惊奇地又看到了@Inject!
原因很快查到了,一个刚刚结束了婚假回到开发团队的开发人员,因为错过了关于这次修改的讨论,重新在代码中引入了@Inject。
我们的架构师设计出的精美架构受到了污染。这种事情以前也发生过,似乎总是有各种各样的原因,导致架构在实现者手里变形。我们的架构设计师能设计故事的开始,却猜不中故事的结局。这种情况是他所痛恨的。
“我们需要有一种手段,能对架构的实现进行监控。”架构师在开发例会上说,“这不是不相信我们的程序员,人都可能犯错的。”
“信任,但要核查。”里根在签署核裁军条约时曾这么说。
一个对敏捷软件开发颇有研究的高级程序员说,“我最近看了一本书,书名是《持续集成》,书中提到了一个工具,好像叫JDepend,可以显示项目中的包依赖关系。正适合解决这个问题。”
后来的故事简单了,通过重构,我们将Factory放在了单独的一个包里,只有这个包才对Guice产生依赖。我们的架构师经常地查看JDepend的输出,看着他设计架构一天天地实现。
尾声
我们的架构师盯着他的二十二寸宽屏液晶显示器,回味着简洁优美的架构所带来的快感。一个念头在他脑海中闪现:“如果Guice不提供@Inject相关的功能,那Jar包就会更小了。”
Guice是开放源代码的,如果有时间,我们的架构师将替他的“新娘”瘦身,去掉@Inject相关的所有类,让这种架构污染永远也不可能再发生。但是现在,还有更多的项目工作等着他。也许等他忙完这段时间,会再来折腾一下这个问题。
生命在于折腾,不是吗?
分析代码和项目管理:Sonar
第一,对多个项目的统一管理和集中展示。这适合在开发组织(乙方)中应用,特别是一些做外包的公司的QA部门。
第二,对主要测量指标的时间维度的关注。当前的状况固然重要,但更重要的是发展的历史和趋势:情况是在变得越来越好还是越来越糟?。
第三,单个项目管理的dashboard。把项目的主要测量指标以图表(仪表盘)的方式展现出来。
第四,向下钻取具体信息。允许进一步关注细节。
第五,覆盖云。用一种创新的方式体现被测试代码中各个类被关注的程度。这种方式似乎来自于Blog对Tag的一种管理,例如,Sun公司的博客中的Most Popular Tags。很直观。
关于CMM有一个简单易记的说法:第二级是“Plan the work”,第三级是“Work the plan”,第四级是“Measure the work”,第五级是“Work the meature”。Sonar给了我们一些第五级的启示:我们关注哪些项目测量指标?怎样展现和应对?
Netbeans 6.5和Liferay 5.1.2
Portal和portlet的概念很适合企业应用的组件化开发、SOA和流程再造,为应用程序的表示层提供了灵活的解决方案。是好东西。
2009年1月6日星期二
FDD项目进度管理和停车场图
每一个特征的实现分成两步:根据特征设计(DBF)和根据特征构造(BBF)。其中包含一些小的里程碑,每个里程碑代表着完成了一定百分比的工作量。领域走查:1%;设计:40%;设计审查:3%;编码:45%:代码审查:10%;提交构造:1%。
接下来的魔法就是由项目管理人员来告诉大家项目的进度。最具创新的就是“停车场图(Parking Lot Char or Parking Lot Diagram)”。下面是一个例子 :
绿色的部分代表已完成的特征集,红色的部分代表已延期特征集,蓝色的部分代表正在开发的特征集,白色的部分代表还未开始的特征集。点击图片可以链接到该图的原出处( 向Jeff De Luca致敬)。
怎样能画出这样的停车场图?好东东来了,你可以在这里下载一个Excel模板。
每个星期打印一张这样的图,贴在墙上,告诉所有感兴趣的人,我们工作做到哪儿了。另外,还可以打印已完成特征数(或包含总特征数)随时间变化的折线图。看到这样的图表,所有的人都会心情愉快的。