2009年6月12日星期五
《彩色UML建模》译者序
人们的学习都是从模仿开始的。学习书法时,一种重要途径就是临贴。学习围棋时,一种重要途径就是打谱。学习面向对象分析和建模时,对应的途径在哪里?
开发者希望看到真实世界中企业级开发的例子,而不只是ATM机的UML图。如果您是一名建筑设计师,您有机会看到金茂大厦的设计图纸吗?我们会看到最后的产品,但对产品得到的过程一无所知。模型并不重要,重要的是得到模型的过程。我们希望听到更多的设计大师的自战解说。开源让我们有机会看到优秀的代码,而这本书让我们有机会看到优秀的面向对象分析模型,以及建模的过程。
建模能帮您做到什么?或者说,您为什么要建模?对我来说,建模是为了让软件开发更简单一些。虽然让复杂的事情变得简单并不容易,但这本书却做到了。通过4种彩色的架构型,让我们能够迅速地对复杂的业务领域建立起简单的模型。
系统分析师需要“体察千行百业之要义”,这本书对企业组件的全景式描述,为我们提供了学习的典范。
Booch在他的《面向对象分析与设计》一书中说,所有成功的软件项目都有两个显著的特点:一是很强的架构愿景,二是迭代增量式的开发。彩色UML和FDD做到了这两点。
一本好书会改变您对事物的理解,从而改变您的行为实践。对我来说,这本书就是这样的。每次我看到其他人给出的领域分析模型,都会用彩色UML的方法去印证,结果总是能得到更好的模型。
这本书是一本值得反复临写的贴,是一本值得反复打的谱。
在这本书的翻译过程中,我学到了很多,因此郑重地向大家推荐它。如果这本书对于您改进软件开发实践有所帮助,我将十分高兴。
开发者希望看到真实世界中企业级开发的例子,而不只是ATM机的UML图。如果您是一名建筑设计师,您有机会看到金茂大厦的设计图纸吗?我们会看到最后的产品,但对产品得到的过程一无所知。模型并不重要,重要的是得到模型的过程。我们希望听到更多的设计大师的自战解说。开源让我们有机会看到优秀的代码,而这本书让我们有机会看到优秀的面向对象分析模型,以及建模的过程。
建模能帮您做到什么?或者说,您为什么要建模?对我来说,建模是为了让软件开发更简单一些。虽然让复杂的事情变得简单并不容易,但这本书却做到了。通过4种彩色的架构型,让我们能够迅速地对复杂的业务领域建立起简单的模型。
系统分析师需要“体察千行百业之要义”,这本书对企业组件的全景式描述,为我们提供了学习的典范。
Booch在他的《面向对象分析与设计》一书中说,所有成功的软件项目都有两个显著的特点:一是很强的架构愿景,二是迭代增量式的开发。彩色UML和FDD做到了这两点。
一本好书会改变您对事物的理解,从而改变您的行为实践。对我来说,这本书就是这样的。每次我看到其他人给出的领域分析模型,都会用彩色UML的方法去印证,结果总是能得到更好的模型。
这本书是一本值得反复临写的贴,是一本值得反复打的谱。
在这本书的翻译过程中,我学到了很多,因此郑重地向大家推荐它。如果这本书对于您改进软件开发实践有所帮助,我将十分高兴。
2009年6月10日星期三
Top down还是Bottom up?
设计程序或写程序,您是习惯自顶向下还是自底向上?
一个朋友曾对我说,很感谢父亲小时候对他的作文训练。训练的方法是这样的:扩写和缩写。把一句话扩写成一段话、一篇文章。把一篇文章缩写成一段话、一句话。这就是所谓的演绎和归纳,也是自顶向下和自底向上的方法。
Alistair Cockburn在《编写有效用例》时也表达了这样的观点:所有的系统都可以归纳为一个最大粒度的用例,即“使用XX系统”。然后我们再把它逐步细化,得到“天空级用例”、“海平面级用例”、“水下级用例”。
这是自顶向下的分析方法。但也有不同观点。
Martin Fowler在《设计已死?》中传达出这样的观点:告诉我一个一个的用户故事,最后我会给你一本故事集。这本故事集就是它存在的意义所在,你不必事先告诉我这本故事集“一言以蔽之”应该怎么说。我可以通过不断的消除重复和重构,得到高级的抽象概念。
但是Grady Booch在《面向对象分析与设计》中告诉我们,人们对客观世界的认识往往是从中间的抽象概念开始的。例如小孩在认识动物时,开始是认识狗,然后会向下认识拉布拉多、吉娃娃,向上认识哺乳动物、动物。
我们在写文章时也是如此。先会有一段素材,让你在脑子里闪过一个念头,涌出一股写作的冲动。然后你开始仔细思考,提练更深刻的主题,寻找更多的素材,向抽象和具象不断探索。
我在设计系统时也是如此。
一个朋友曾对我说,很感谢父亲小时候对他的作文训练。训练的方法是这样的:扩写和缩写。把一句话扩写成一段话、一篇文章。把一篇文章缩写成一段话、一句话。这就是所谓的演绎和归纳,也是自顶向下和自底向上的方法。
Alistair Cockburn在《编写有效用例》时也表达了这样的观点:所有的系统都可以归纳为一个最大粒度的用例,即“使用XX系统”。然后我们再把它逐步细化,得到“天空级用例”、“海平面级用例”、“水下级用例”。
这是自顶向下的分析方法。但也有不同观点。
Martin Fowler在《设计已死?》中传达出这样的观点:告诉我一个一个的用户故事,最后我会给你一本故事集。这本故事集就是它存在的意义所在,你不必事先告诉我这本故事集“一言以蔽之”应该怎么说。我可以通过不断的消除重复和重构,得到高级的抽象概念。
但是Grady Booch在《面向对象分析与设计》中告诉我们,人们对客观世界的认识往往是从中间的抽象概念开始的。例如小孩在认识动物时,开始是认识狗,然后会向下认识拉布拉多、吉娃娃,向上认识哺乳动物、动物。
我们在写文章时也是如此。先会有一段素材,让你在脑子里闪过一个念头,涌出一股写作的冲动。然后你开始仔细思考,提练更深刻的主题,寻找更多的素材,向抽象和具象不断探索。
我在设计系统时也是如此。
2009年6月9日星期二
RESTful的codebase
我们的代码集曾经保存在不同的地方,现在终于进入了云端。Bespin已经在云上做IDE了。
毫无疑问,代码是很多人关注的一项资源,而资源可以用RESTful的方式来表现。所以可以有这样的URL:
毫无疑问,代码是很多人关注的一项资源,而资源可以用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的课程表
一个连锁健身会所在全国有70多家分店,每个店都要有一张课程表。课程表中包含一周的课程、操房、教练等信息。如果客户感兴趣,还可以进一步了解课程介绍、操房介绍和教练介绍等信息。课程表会不定期进行变更,客户可以在网页上查看,也可以下载PDF文件。
如果按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、容易实现缓存机制
如果按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页,就把书一扔,找人对杀去了。杀是杀得很爽,但我那时连征子都还没学到,实战中另一个同学“教育”了我。围棋也有数千年的历史,王积薪、黄龙士、施范、秀策、木谷、吴、六超、聂马、曹李,都是我们可以效仿的大师。
一般来说,要想提高棋力,需要日复一日地做三件事:打谱、做死活题、实战。打谱是一种临摩,做死活题是一种临摩,实战也是一种形式的临摩。
另一点值得强调:实战一定要复盘。不论结果如何,要找出下得好的棋和下得臭的棋。在水平低的时候,如果有条件,最好是找水平高的老师来帮忙复盘。这就是老师的作用:虽然他的水平没有大师那么高,但是他可以告诉你练习的方法(需要去临摩哪些东西),也可以指出你最需要改进的地方。可惜我现在明白了怎么学习围棋,却没有太多的时间去练了。
如果想学习管理,可以去看看朱兰、克劳斯比、戴明、德鲁克、高德拉特、韦尔奇,再找个咨询师。如果想练习游泳,可以去看看波波夫、北岛康介、菲尔普斯的视频,再找个专业教练。
如果您是设计软件的,梦想成为一名伟大的程序员,那该怎么做?您一定已经知道了。
就这样几年之后,儿子写的字有点像样子了。汉隶临的是《张迁碑》,行书在临《兰亭序》和《集字圣教序》。诗云:如切如磋,如琢如磨,大概就是这个意思。书法艺术有几千年的历史,从二王、索靖、张芝、智永、欧储颜柳、张旭、怀素,到苏黄米蔡、赵孟頫、徐渭、王铎,出现了为数不少的大师。不仅他们的作品值得我们反复临习,他们成为大师的艺术追求之路,也同样值得我们借鉴。
米芾的例子就很典型。米芾习书,自称“集古字”。根据米芾自述,在听从苏东坡学习晋书以前,大致可以看出他受五位唐人的影响最深:颜真卿、欧阳询、褚遂良、沈传师、段季展。米芾有很多特殊的笔法,如“门”字右角的圆转、竖钩的陡起以及蟹爪钩等,都集自颜之行书;外形竦削的体势,当来自欧字的模仿,并保持了相当长的一段时间;沈传师的行书面目或与褚遂良相似;米芾大字学段季展,“独有四面”、“刷字”也许来源于此;褚遂良的用笔最富变化,结体也最为生动,合米芾的脾胃,曾赞其字,“如熟驭阵马,举动随人,而别有一种骄色”。然后他开始学习二王。《宋史》载:米芾“妙于翰墨,沉著飞翥,得王献之笔意;尤工临移,至乱真,不可辨……”。最后,“既老始自成家,人见之,不知何以为主”,完成了自己风格的确立,大概在五十岁以后。
所以,成为书法家的过程可以总结为四个字:入帖、出帖。
张大千则是画家中的典型例子。他早年临石涛临到乱真,号称“当代石涛”。四十多岁时自费赴敦煌,花了大量时间对十六国、北魏、北周、隋、唐、五代、宋、西夏、元各朝的壁画代表作及雕塑进行了临摹。从此,张大千的画风也为之一变,善用复笔重色,高雅华丽,潇洒磅礴,被誉为“画中李白”、“今日中国之画仙”。他走的路和米芾一样:从临摩开始,到乱真,最后自成一家。
我读中学时,正好是聂卫平在中日围棋擂台赛上大胜的时候。全国上下掀起了一阵围棋热。我也去弄了一本电视围棋教程来看,我记得很清楚,我只看到了第37页,就把书一扔,找人对杀去了。杀是杀得很爽,但我那时连征子都还没学到,实战中另一个同学“教育”了我。围棋也有数千年的历史,王积薪、黄龙士、施范、秀策、木谷、吴、六超、聂马、曹李,都是我们可以效仿的大师。
一般来说,要想提高棋力,需要日复一日地做三件事:打谱、做死活题、实战。打谱是一种临摩,做死活题是一种临摩,实战也是一种形式的临摩。
另一点值得强调:实战一定要复盘。不论结果如何,要找出下得好的棋和下得臭的棋。在水平低的时候,如果有条件,最好是找水平高的老师来帮忙复盘。这就是老师的作用:虽然他的水平没有大师那么高,但是他可以告诉你练习的方法(需要去临摩哪些东西),也可以指出你最需要改进的地方。可惜我现在明白了怎么学习围棋,却没有太多的时间去练了。
如果想学习管理,可以去看看朱兰、克劳斯比、戴明、德鲁克、高德拉特、韦尔奇,再找个咨询师。如果想练习游泳,可以去看看波波夫、北岛康介、菲尔普斯的视频,再找个专业教练。
如果您是设计软件的,梦想成为一名伟大的程序员,那该怎么做?您一定已经知道了。
2009年6月6日星期六
面向对象设计与开发(两天培训提纲)
一、好的面向对象系统的特点
二、设计模式
三、面向对象系统开发过程
四、领域分析与建模
五、相关技术
- 高内聚、低耦合原则
- 关注点分离原则
- SOLID原则
- Don’t Repeat Yourself!
- 加入一个中间层!
- 复杂系统的架构方式
二、设计模式
- 设计模式的意义
- 设计模式的模板
- 设计模式选讲
三、面向对象系统开发过程
- 从需求到OOA
- OOA到OOD
- 自动化测试和持续集成
- 敏捷开发方法学
四、领域分析与建模
- 领域驱动开发
- 彩色UML方法
- 案例分析
五、相关技术
- 对象持久设计
- 表示层设计
- 组件化
- AOP
- SOA和SaaS
- RESTful和AJAX
- MapReduce集群
2009年6月4日星期四
《执行SOA》上市了
执行SOA--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迁移。
译者序
几年前,为了尝试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日星期日
改进实现
TimeMachine的第一个实现跑起来了,跟预期的行为一样。但进一步想想,有两点不足:
- 每睡一小时/一分钟都会输出一条日志,一个周末下来,输出了很多重复的日志。
- 端午节要到了,原来的实现只考虑了周末,没有考虑这种节假日休息,周日反而要工作的情况。
- 在WorkTimeControllerImpl中添加一个方法“Date nextStartTime()”,计算下一次开始工作的时间,这样waitTillStartTime()就可以只输出一条日志,然后一直睡到下次开始工作。
- 在WorkTimeControllerImpl中添加一个非工作日列表,包括正常周末和节假日。
- 重复的无用信息是不好的。Don't Repeat Yourself. 无用的信息会干扰有用的信息。
- WorkTimeController和TimeMachine接口都不需要改变,这就是区分“做什么”和“怎么做”的好处。
2009年5月21日星期四
TimeMachine
手上在写的程序希望能够跑着就不要人管,但只在工作日的9:00到11:30,13:30到15:30做需要做的事,其他时间休息。
所以我设计了一个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方法则依次返回一组预先设定好的时间。这样,我们就得到了一个时光机,可以检测在一些关键的时间点程序行为是否正常。
时间是一个关注点,在这个例子里,我们实现了时间关注点的分离,改善了程序的可测试性。
所以我设计了一个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建过领域模型!
特定的Info触发Strategy创建Executing,Executing在被创建之后可以接受Info,并创建ExecutingDetail。Strategy可以控制在执行的Executing的数目。Executing实际上是一个有限状态自动机的实例。
画了类图以后,感觉好多了。方法学就是习惯。习惯就是不那么做就感觉不舒服。
试着用Netbeans画了个序列图。VisualParadigm接手UML模块后,带来了易用性。
2009年5月10日星期日
最大价值
“如果你的男朋友跟你谈恋爱三个月了,还没有谈到未来,那么你们就没有未来”。这是以前看到的一个笑话。现在我改编了一个新版本:“如果你的项目开始三个月了,还没有提供客户价值,那就永远不能提供客户价值”。
这句话很极端,我相信一定能找出反例。所以如果您不同意,一笑而过就可以了。
早交付、常交付客户价值,是敏捷方法学的核心。这样做的直接结果就是提高了ROI。所以,向商业客户推销敏捷方法是不难的,只要告诉他们可以提高ROI就可以了。
体现客户价值的需求有很多项,先交付哪一项?答案很简单,先交付最有价值的那一项。如果你运气好,最有价值的那项需求碰巧可能是最容易用计算机系统实现的。那项需求如果用人工来做,成本可能极大,精度和时间上可能完全做不到;但对于计算机系统,则是轻而易举的事。
但是,要明白什么是客户的最大价值,需要和客户进行充分地沟通。要让客户了解计算机系统可以做到什么。如果客户对计算机系统缺乏了解,就不能提出计算机系统对他最大的帮助是什么。开发者要尽可能了解客户的业务特点,和客户一起,找出计算机系统能够实现的最大价值。
鼠标的发明者Douglas Engelbart说,计算机的作用是IA(人类智能的扩展)。我们要找到人的弱点和计算机的长处,然后取长补短。
七个习惯说“要事第一”,TOC理论说“找到瓶颈并加以改善”,棋谚说“急所先于大场”,道理都是类似的。可做的事情有很多,做不完,要列出优先级。
宫本武藏在《五轮书》说:以剑法而言,敌一人与万人其理雷同。做一个人的小项目和做一万人的大项目,道理是一样的。
结论:定制开发的成本是很高的,客户的价值也很多,我们要尽可能从最大价值开始。
PS:问:“不太理解这里客户价值指什么。是指客户最关心的feature吗?”
答:要让客户享受到实实在在的好处,挣到钱。这样客户就愿意掏钱支持下一步的开发。大家拿到钱,士气才会高。
就像医生开药,不能说我的药吃两年,让你壮得像小伙。得是吃了一个月之后,关键症状有明显减轻。然后再说,坚持吃点啥,调理调理。也有可能吃了一个月的药后,后面的药都不用吃了。
实际上,随着客户业务的发展,不断会有新的业务瓶颈出现,需要克服。IT系统也可以不断改进,提供新的客户价值。于是实现了双赢。
所以在客户关心的feature中,弄清楚每个feature能帮客户挣(省)多少钱。这是提供解决方案的基础。
2009年5月6日星期三
美丽架构7原则
- 一处一事实。一个设计决定,只出现在一个地方。将来当这个设计决定改变时,改动量可以最少。参见设计决定、反悔、霰弹式修改和架构污染。
- 自动传播。有时候出于效率考虑,必须复制一些东西。系统需要保证这些复制很容易自动进行。现在流行的分布式版本控制系统(Git、Hg)就是很好的例子。还记得当初的EJB吗?事实是我们在写分布式应用,然后规范要求我们在几个地方尊重这个事实。EJB3.0改变了这种情况。
- 架构也包含构建过程。架构什么都不是,围绕架构的过程就是一切。
- 使用最少的机制。够用就好。要明确目标,优化瓶颈,不要沉迷于非瓶颈部分的优化。要事第一。
- 设计引擎。利用引擎,我们把该放在一个地方的内容放在一个地方。例子有规则引擎、工作流引擎、脚本语言和DSL。但是在项目中自己实现一个引擎要考虑实现的成本和项目的经费。
- 支持伸缩。在负载增大的情况下,系统的表现如何?系统是怎样的方式实现伸缩?
- 抵制熵增。年轻时很美并不难,难的是一辈子到老都很美。美丽的架构能够经受时间的考验。
2009年5月4日星期一
DSL
不同领域的人讲着不同的语言。这些语言中的概念有特殊的语境,包含一些特殊的概念,是对话者多年潜心钻研的结果。
在考虑系统架构时,一般原则是按领域来分解系统。不同的系统组件由不同的领域专家来负责。例如,我们把系统分成三层:UI、业务、持久。
将不同领域的内容写在一段程序里,是公认的坏事。如果你看到某段代码中既有对HttpRequest的处理,又有业务规则,还有数据库连接和SQL语句,那么有两种可能:写这段程序的人是初学者或超高手(初学者不知有更好的写法,而超高手故意为之,因为写这段代码时的情形令他做出这种选择)。
业务领域可以继续划分,在一个企业里,财务、销售、生产、仓储物流等等都是不同的领域,拥有各自的领域专家。
架构师,是那种对每个领域都懂一点的人。而他懂的那一点,恰恰是这个领域的精华。这样,他就能设计出不同的领域如何组织成一个系统。
企业的CEO,也是那种对每个领域都懂一点的人。他甚至还懂信息技术。所以他知道怎么把属于不同领域的部门组合起来,形成一个系统,去实现整体的目标。
面向对象技术的强大就在于,可以形成自定义的概念抽象。然后,我们可以有一个执行引擎,执行由这些概念所组成的指令。
Groovy/JVM是个好引擎。
if ((new PriceDifference("zn0906", "zn0907") > 108)
&& new Tendency("zn0906").isUp())
{
new KaiCangBuyIn("zn0907");
new Delay(ONE_SECOND);
new KaiCangSellOut("zn0906");
}
没有仔细学过Groovy,如果能把这些new去掉就更清晰了。
Google:广告界最懂信息技术的公司
Google是著名的搜索引擎,但搜索是免费的,它也不搞竞价排名。现在大家都知道了,它的盈利模式是广告业务。开展广告业务的公司很多,Google的核心竞争力在于,它是所有广告公司中最懂信息技术的。
早就听说Walmart建立了强大的数据仓库,通过数据分析来制定各种业务决策:商品的摆放位置、销售价格......,Walmart不记录客户的姓名,但是它的系统被一本讲客户关系管理的书列为重点案例。Tim O'Reilly在推销Web 2.0的概念时说:你们知道吗?Web 2.0最成功的典范不是Sun的员工blog,也不是Dell的产品评论,而是Walmart。因为它能根据销售信息决定采购、物流、上架。也就是说,它能根据环境变化来自动改变业务决策。这些环境变化的信息正是客户所提供的,这就是Web 2.0的精髓。Walmart是所有零售公司中最懂信息技术的。
英文资料常见到这样一个词组:informative decision,意为“信息充分的决定”。然而,环顾四周,这样的决定是少之又少。
做IT这行的人和公司,对信息技术的理解也有高下之分。作为一个软件公司的管理层或一个软件项目的经理,他们的日常决定有多少是informative decision?
谁是软件界最懂信息技术的公司?谁是金融界最懂信息技术的公司?谁是健身行业最懂信息技术的公司?
一般来说,最成功的人是拥有最好信息的人。
2009年4月29日星期三
Endless Test, Continuous Integration
“无尽的测试,持续的集成。”这句话在2000年左右敏捷开发刚兴起的时候大家讲得比较多,这两年已经不大听得到了。但是,好的东西总是让人不断认识到它的价值,直到流淌在血液中,成为自己有机的组成部分。
方法学就是习惯,就是不这么做你就觉得很不舒服。
没有什么比好的理论更可实践的。敏捷开发关于测试和集成的理论正是如此。现在我会:
- 让客户确定里程碑。
- 写出业务测试用例。
- 扫除一切障碍,实现业务用例。
特征驱动开发(FDD)提到,它的一个优点就是制定对客户有意义的里程碑。客户可能不懂IT技术,但客户通常会明白什么对他们自己有价值。作为软件开发人员,你不用猜测、假设什么东西对客户有价值。相反,你直接问客户:您最终希望得到什么结果?您希望一个月得到什么结果?
作为技术人员,我们很容易假定一份完备的需求规格说明书对客户是有价值的,一叠UML模型对客户是有价值的,一个架构设计对客户是有价值的。然而,这种假定是错误的。并不是所有的客户都这样想。现在的商业用户越来越重视上市时间。换言之,投资要快一点看到回报。在造一幢大楼时,恨不得第二层还在造,第一层就已经租出去了。
人们对未来的长期预测能力比较差,但对近期的预测能力还是可以的。“明天的天气跟今天差不多”,大多数时候都是对的。就这样,一天天的差不多,在三个月后会有一个大变化,在六个月后变化更大。
FDD说,作为项目经理,不要去问开发者进度。让开发过程自动报告进度。最好是让客户告诉你,我们的项目完成了百分之多少。昨天,一个客户告诉我,他觉得我们的项目已经成功了30%。听得出,他是满意的。
在询问客户之后,把你的理解写成一个可执行的业务测试用例,跟客户确认。如果你写不出来,或者客户不认同,那就是与客户的沟通还有问题。不解决这些问题就开始开发,做出来的东西一定不能满足客户的要求。
确认测试用例之后,施展你的才华的时候就到了。尽你所能,又快又好地实现它。下个星期(月)给客户去演示、发布、上线。
举个例子,如果你想做一个Web 2.0的网站。打算集成SocialSite、JForum、Roller、XWiki...那么你会怎么做?我会先确定用户管理和单点登录机制,然后是无尽的测试和持续的集成。
远期目标清晰,近期目标明确,辅以无尽的测试和持续的集成,大事成矣!
2009年4月24日星期五
改造多线程爬虫
我曾经为了好玩写过一个多线程的网页爬虫,最近重新思考了一下多线程,想对这个爬虫的设计做一些改动。
程序的基本业务如下:
- 从一个待爬集合中取一个待爬URL
- 取回这个URL代表的Web页面
- 对页面进行解析,找出其中的链接URL
- 如果找到的URL不在已爬集合中,就把它放到待爬集合中去
我关心的这些URL都属于同一个网站,所以从一个URL开始(如首页),如果待爬队列中过了一段时间没有新任务,整个网站就爬完了。
由于网络访问的延迟,所以采用多线程是很自然的考虑。但是多线程的设计并不能伸缩到多台机器的集群上。“做什么(爬网页)”和“怎么做(多线程)”混在了一起,未能实现关注点分离。
如果我们决定使用多线程,可以。但要确保这种设计决定没有散布在程序的各个角落。不幸的是,我原来的程序没有做到。由于要使用多线程,我在程序的各个角落使用了synchronize关键字,还使用了线程安全的集合类。
之所以要使用synchronize关键字和线程安全的集合类(手段),是因为我要保证操作的原子性(目的)。实际上,根据实现的设计决定不同,就会采用不同的手段。例如,我们可以通过数据库持久事务来实现操作的原子性。
按照Google MapReduce的设计思路,我们应该设计一个任务主控组件,它负责分发任务和结果合并。这个主控组件管理着待爬集合、正在爬集合和已爬集合。同时它也管理着执行机器的集合。它组装出一个个的Runnable或Task,发送到执行机器上(如果只有一台机器,那就本机了),并监督它们是否正常执行。可以向一台执行机器同时发送多个任务,在执行机器上实现多线程。
这样,通过一个中间层,一个子任务的执行和总任务的管理之间的耦合解除了。
这个架构可以移到GAE上去。写一个RESTful的Web service,负责执行一个子任务。任务主控组件向GAE上的这个应用发起一堆请求,利用GAE的强大计算力、带宽以及伸缩性。也许一万个页面只要几秒钟就搞定了。
你说什么?这个任务主控组件不好弄?本身就需要很好的带宽?把它也弄到GAE上去!
网络就是计算机。这一天到来已经很久了。
2009年4月18日星期六
多线程、Project Darkstar、MapReduce和GAE
通过提高主频来提升性能的时代结束了,我们一下子就被扔进了多核、集群的时代。程序员可以分成两种:1.会分布式并行编程的;2、不会分布式并行编程的
有人声称,继OO之后,程序员下一项需要掌握的技术就是多线程技术。但是我预计,短时间不会有大量程序员掌握多线程编程,就像短时间学不会OO技术一样。
IT业界从来不缺聪明的人,他们已经设计了各自的解决方案。让不懂分布式并行编程的人享受到分布式并行的好处,就像让不懂OO的人享受到OO的好处一样。
“Beautiful Architecture”一书的第3章介绍了Project Darkstar,它为多玩家在线游戏和虚拟世界这样的系统设计了一种架构,使得游戏程序员不需要掌握分布式并行程序设计技术。
GAE不让你启动自己的线程,所有自己会启动线程的jar包都不能跑在GAE上。伸缩性和并发由底层基础设施来实现。
我们需要重新考虑一下应用的架构方式了。如何才能够跑在GAE这台巨大的虚拟机上?
2009年4月15日星期三
2009年4月14日星期二
“大”项目的关键是集成
一个朋友提到,他们的公司将clear case换成了git。而他的感觉是,由于git是分布式版本控制系统(DVCS),所以在几百人的分布式大项目中,如果弄不好,很快就会乱套。虽然有一个“主代码库”,但是可以想象,提交时的冲突会很多。
确实,Clear Case有很多先进的特征,我也很喜欢,除了它的价钱之外。但我认为,这里问题的关键是集成。
几百个人的分布式大项目,要让所有人的工作能够顺利集成在一起是一件不容易的事情。其中的难度早有定论:这些人需要沟通协作,而沟通时的不一致和冲突将耗费大量的时间和精力。所以,在大项目中,我们常常看到集成的工作量比编码的工作量要大,有时候甚至大得多。
开发人员多是一种类型的“大”项目,另一种类型的“大”项目是小团队,但复用了很多第三方的软件。这种类型的项目在开源软件中相当常见,比如Liferay就是一个例子。它使用了Velocity模板框架,提供与多种应用服务器的绑定,支持多种关系数据库后端,还支持第三方的CMS和用户管理。
这两种大项目,如何来实现集成?
据我所知,以前某些大公司的做法是,项目设置build manager或build team,专门负责集成构建。这样做的含义很清楚:集成工作量很大,我们要专派人手。
但现在业界的最佳实践是持续集成。
对于开发人员很多的项目,《持续集成》中介绍的一种集成方式或许可以解决他们的问题。设置两个Repositry,大家向第一个Repositry提交,如果提交后5分钟没有新的提交,就第一个Repositry上持续集成。如果集成失败,自动回退到上一次成功集成的状态;如果集成成功,将新提交的内容再提交到第二个Repositry。开发人员将在持续集成服务器上,看到自己的提交是否成功集成。也会在集成失败时收到通知。
(这个故事再次告诉我们:工具的价值小于过程的价值,过程的价值小于人的价值。人是改进过程和工具的决定因素。)
对于小团队大量复用的项目,持续集成仍是成败的关键。你可以在Liferay项目中看到大量使用Selenium自动化集成测试。
最近我独自一人开发程序时,也明显感到持续集成的重要性:我需要把一周、两周、一月、两月的工作集成在一起。也许,集成一直都比编码更重要吧。
订阅:
博文 (Atom)
