彩色UML图可以用即时贴在白板上设计,也可以使用支持UML图的工具画出。但是使用工具要注意一点,就是设计好的模型应该打印出来,贴在墙上,让经过的人都可以驻足观看,发表意见。这就是敏捷方法学中的信息辐射原理。沟通无时无处不在。
这样做的好处是明显的。每个人都有机会熟悉全部的业务流程,以达到对系统的一致理解。有疑问,早提出。特别是领域(业务)专家,可以不断想起点什么要补充的。
但是有一个问题:类名应该使用英文吗?
和用户沟通有一个重要的原则,要使用用户熟悉的语言和图表。画用户看得懂的UML图。如果用户英文不好,如果用户平时都讲中文,所有的领域词汇都是中文,那么用英文的类名画出的彩色UML图挂在墙上就有点小麻烦了。
有一个改进方案,在类名后面用括号注明中文意义,如:OrderMaking(下订单)类,这是一个红色的时刻-时段。
还留着英文类名是为了写程序的方便。这样我们维护了一个映射关系,即中文领域词汇和英文类名的映射。当然,也可以创造性地使用拼音,如:XiaDingDaning(下订单)。
但是我记得以前试过,在Java里可以直接使用中文类名。所以又试了一下。
public class Main {
public static void main(String[] args) {
下订单 这次下订单 = new 下订单();
这次下订单.确定();
}
}
class 下订单 {
public void 确定() {
System.out.println("Good!");
}
}
哈,可以运行!也许业务层代码这样写更好玩。
2008年12月18日星期四
用例和特征
Alistair Cockburn在他的《编写有效用例》一书中指出,“海平面用例”的特点如下:
所以在大致上,我们可以将特征集与“海平面级用例”对应起来。因为如果“销售员”完成了很多“进行一次产品销售”或做得很好(销售额大),就有理由提出升职或加薪要求。
在特征驱动开发中,特征是
虽然做很多的“计算一次销售总额”不能导致升职或加薪,但这个特征对于客户来说显然是有价值的。所以在大致上,我们可以将特征与“水下级用例”对应起来。
在特征驱动开发中,主特征集是
“天空或汇总级用例”反映了多个人的目标,例如“保险购买、支付和理培”。所以大致上,我们可以把主特征集和“天空级用例”对应起来。
- 一个人、一个地点、一个时间(2至20分钟)
- 完成了就高兴地离开
- 参与者(如果是职员)做了很多这样的事,可以要求获得加薪或升职
- [action](ing) a(n) [object]
所以在大致上,我们可以将特征集与“海平面级用例”对应起来。因为如果“销售员”完成了很多“进行一次产品销售”或做得很好(销售额大),就有理由提出升职或加薪要求。
在特征驱动开发中,特征是
- [action] the [result] [by|for|of|to] a(n) [object]
虽然做很多的“计算一次销售总额”不能导致升职或加薪,但这个特征对于客户来说显然是有价值的。所以在大致上,我们可以将特征与“水下级用例”对应起来。
在特征驱动开发中,主特征集是
- [object] management
“天空或汇总级用例”反映了多个人的目标,例如“保险购买、支付和理培”。所以大致上,我们可以把主特征集和“天空级用例”对应起来。
2008年12月7日星期日
彩色书中文版上市了

彩色书中文版终于上市了!
Peter Coad是我仰慕的前辈大师。大约在1997年,朋友李亮向我介绍了他和Edward Yourdon合作的《面向对象设计》。那是一本小册子,我一看就喜欢上了。全是经验之谈,没有东抄西抄。原书是1991年出版的,中文译本是1994年出版的。
没想到多年以后,以这样的方式接近大师。彩色书的原书是1999年出版的,距现在也快10年了。一年以前,我向推荐华章推荐引进这本书。今天,终于与中国的开发者见面。
在2000年左右,敏捷方法学开始兴起。过了几年,我注意到了敏捷方法学中有一枝是特征驱动开发(FDD)。这种敏捷开发方法有一个特别的地方,它强调整体对象建模,即前端设计。而且采用了一种极有特点的彩色UML建模的方法。然后我看到这种方法背后大师的名字。
网上关于彩色UML的内容不多,但我还是设法搜集了一些。这些年,我一直在研究Java、工作流、开发方法学、UML建模方面的内容。等到我看到FDD和彩色UML,这些内容忽然就贯通了,有一种醍醐灌顶、任督二脉打通的感觉。
好的东西就是你从未想过拥有,但是一旦拥有,就别无所求。彩色UML给我这样的感觉。
有一年Martin Fowler到上海,在交大和林德彰教授讨论软件开发方法学,还发生了一件趣事。林教授把传统方法学比喻为楷书,把XP比喻为草书,认为先要打好楷书基础,然后才能学草书。当时我提出,敏捷方法中的FDD注重前端设计,同时又保持轻量级方法学的特点为,可能是行书。会后,一个白人老外专门跑过来跟我交流,说谢谢我提到彩色UML和FDD,并说他们的公司在张江,在开发时就采用这套方法学,效果很好!
曾经跟华章的编辑开玩笑说,这本书不引进,是中国计算机出版界的羞耻啊。现在我要对他说,功德一件!
2008年12月4日星期四
服务治理
几年前,为了尝试JDK 1.5中的并发包,我写了一个多线程的爬虫程序,利用线程池来分析B2C网站上的商品价格,希望可以做一个shopping.com那样的比价网。
并发200个线程,每个线程从待爬URL队列中取得一个URL,取回网页,进行分析,找出其中的价格信息,再找出其中的URL链接。开发过程很正常,但在测试中遇到了问题。在爬了7万多个网页之后,程序开始越来越慢。凭感觉判断,有一些线程“死”掉了。
多线程的调试并不是件容易的事。这个问题很“难”再现。不是普通意义上的难再现,它每次都会出现。但要跑到7万多URL时,才会出现。我试过将线程池退化到1,想找出什么样的URL会导致线程死掉,但这个代价太大了,因为速度很慢。当时的IDE也缺乏对多线程调试的一些支持。而且即便有支持,可能也不太适合这种情况。后来因为种种原因,那个程序就不了了之了。
最近接触的SOA治理和彩色UML的内容给了我一些启发。我们需要关注服务执行的健康状况和服务执行的时间。例如,我们可以进行这样的改动:
在每个线程领取URL时,记录一个时间戳。在它完成这个URL处理时,再记录一个时间戳。再利用一个线程,对未完成的URL定时检查它的健康程度。如果在很长的一段时间内它还没完成,那么它就有问题。这样我们可以找到嫌疑URL。我们可以对这种URL单独测试,看看是否因为程序的原因,不能处理这样的URL。
或者,我们可以把对应的线程任务杀掉,直接跳过这些有问题的URL。
程序在设计时,要考虑运营维护需求,其中包括迅速故障疹断和纠正能力。这就是传说中的“可用性”需求吧。
并发200个线程,每个线程从待爬URL队列中取得一个URL,取回网页,进行分析,找出其中的价格信息,再找出其中的URL链接。开发过程很正常,但在测试中遇到了问题。在爬了7万多个网页之后,程序开始越来越慢。凭感觉判断,有一些线程“死”掉了。
多线程的调试并不是件容易的事。这个问题很“难”再现。不是普通意义上的难再现,它每次都会出现。但要跑到7万多URL时,才会出现。我试过将线程池退化到1,想找出什么样的URL会导致线程死掉,但这个代价太大了,因为速度很慢。当时的IDE也缺乏对多线程调试的一些支持。而且即便有支持,可能也不太适合这种情况。后来因为种种原因,那个程序就不了了之了。
最近接触的SOA治理和彩色UML的内容给了我一些启发。我们需要关注服务执行的健康状况和服务执行的时间。例如,我们可以进行这样的改动:
在每个线程领取URL时,记录一个时间戳。在它完成这个URL处理时,再记录一个时间戳。再利用一个线程,对未完成的URL定时检查它的健康程度。如果在很长的一段时间内它还没完成,那么它就有问题。这样我们可以找到嫌疑URL。我们可以对这种URL单独测试,看看是否因为程序的原因,不能处理这样的URL。
或者,我们可以把对应的线程任务杀掉,直接跳过这些有问题的URL。
程序在设计时,要考虑运营维护需求,其中包括迅速故障疹断和纠正能力。这就是传说中的“可用性”需求吧。
2008年11月30日星期日
从SoftCon归来
两天的中国软件技术大会结束了,收获不少。
潘加宇的“提升技能,准备过冬”是很有启发的,提出的一个公式让人印象深刻:利润 = 需求 - 设计。需求是客户愿意付钱的东西,满足需求越多越好,就可以收越多的钱。设计是实现的成本,是费用。如果可以用一个简单、灵活、有弹性的设计来不断满足客户日益增长的需求,你的利润空间就大了。
由此让我重新思考了架构的意义。架构的弹性,即适应变化的能力,确实是好的设计的核心。业务架构和技术架构都很重要。周日午餐时遇到上海某证券信息公司的技术人员,聊到他们的系统,需要支持大量(30G左右)数据的高并发查询。目前已采用了全内存的方式,但持久仍然使用的是关系数据库。痛苦的地方在于,每次重启要将30G数据加载到内存,这个时间非常长,而且客户是不能忍受的。只好做了双机的架构,切换之后,再重启其中一台。如果有一个好的架构,这样的痛苦和成本,就不必承受了。从文本文件加载数据的速度大约可以是1秒钟1G,这样重启的时间可以压缩到1分钟之内。如果客户仍然不能接受,还可以上双机高可用方案。数据库是个好东西,但不是每种场合都适用。
把数据加载到内存后,再使用LINQ这样的解决方案,就能够提供方便的查询。企业信息管理人员,应该好好考虑一下架构迁移。
本次大会的另一个收获就是和徐锋住一个房间,发现我们有许多共同的兴趣,包括软件、围棋和阅读经典。谈到深夜。有朋自远方来,不亦乐乎!
潘加宇的“提升技能,准备过冬”是很有启发的,提出的一个公式让人印象深刻:利润 = 需求 - 设计。需求是客户愿意付钱的东西,满足需求越多越好,就可以收越多的钱。设计是实现的成本,是费用。如果可以用一个简单、灵活、有弹性的设计来不断满足客户日益增长的需求,你的利润空间就大了。
由此让我重新思考了架构的意义。架构的弹性,即适应变化的能力,确实是好的设计的核心。业务架构和技术架构都很重要。周日午餐时遇到上海某证券信息公司的技术人员,聊到他们的系统,需要支持大量(30G左右)数据的高并发查询。目前已采用了全内存的方式,但持久仍然使用的是关系数据库。痛苦的地方在于,每次重启要将30G数据加载到内存,这个时间非常长,而且客户是不能忍受的。只好做了双机的架构,切换之后,再重启其中一台。如果有一个好的架构,这样的痛苦和成本,就不必承受了。从文本文件加载数据的速度大约可以是1秒钟1G,这样重启的时间可以压缩到1分钟之内。如果客户仍然不能接受,还可以上双机高可用方案。数据库是个好东西,但不是每种场合都适用。
把数据加载到内存后,再使用LINQ这样的解决方案,就能够提供方便的查询。企业信息管理人员,应该好好考虑一下架构迁移。
本次大会的另一个收获就是和徐锋住一个房间,发现我们有许多共同的兴趣,包括软件、围棋和阅读经典。谈到深夜。有朋自远方来,不亦乐乎!
2008年11月23日星期日
对象初始化和Giuce trick
在使用POJO组件对象时,常常需要在对象实例化之后,调用该对象的某个初始化方法。例如,
PersistentLayer.init()
我们在持久层里准备一些初始的业务数据,如第一个超级用户和一些数据字典。
在Spring框架中,提供了这种post-constructor的初始化方法调用机制,但Guice却没有。Guice甚至不理会无参构造方法。(两点说明:1. 在构造方法中完成初始业务数据的准备不太妥当,构造方法应该保持简单,尽量不要加入业务逻辑。2. Guice不执行无参数构造方法,我只是简单试了一下,确实如此。Guice的这个feature肯定有自己的考虑,需要读源码确认,并查一些资料,了解背后的设计考虑。)
这个问题困扰了我们半个小时,考虑过两种绕过去的方法。
一是在封装Guice的Factory中,对对象实例进行处理,如果对象有init()方法,就执行一下。这种方法并不漂亮,涉及泛型、反射,有点复杂。
二是在应用代码中每次请求PersistentLayer时,调init()方法,设置一个静态开关变量,记录是否已经初始化过。同样,这种方法不漂亮,涉及了很多重复。将一个隐含的假定(使用PersistentLayer时,必须先调用init()方法)扩散到了应用代码的各个地方,这种重复是不能容忍的。
最后找到了一个解决方案,即使用Guice的“绑定到实例”的能力。在MyModule.class中,
PersistentLayer pl = new PersistentLayerMemImpl();
pl.init();
binder.bind(PersistentLayer .class).toInstance(pl);
网上有人提到“Guice目前来说还只是个玩具”,提出如下问题:
“不可否认Guice是一个设计精巧的IoC容器,速度也很快,但是这个容器连最基本的对象生 命周期管理都没有,也就是要提供JSR-250里面javax.annotation.PostConstruct和PreDestroy的功能,有人提 出用无参数的@Inject方法来模拟init,这样明显是对Annotation的滥用,用构造参数注入然后在构造方法里面做初始化,这虽然是一种解决 方法,但是有的情况下对象可能需要reinitialize,这样还是需要把初始化单独做为一个方法,用finalize方法来做destroy更不可 取,没法控制什么时候执行.这个问题去年三月就有人提出来了到现在还没有解决,Guice的开发者还是不够活跃,这里这么多人捧Guice,不知道有多少人正式用在项目中.”
我想这种方法也许是一种解决方案。
PersistentLayer.init()
我们在持久层里准备一些初始的业务数据,如第一个超级用户和一些数据字典。
在Spring框架中,提供了这种post-constructor的初始化方法调用机制,但Guice却没有。Guice甚至不理会无参构造方法。(两点说明:1. 在构造方法中完成初始业务数据的准备不太妥当,构造方法应该保持简单,尽量不要加入业务逻辑。2. Guice不执行无参数构造方法,我只是简单试了一下,确实如此。Guice的这个feature肯定有自己的考虑,需要读源码确认,并查一些资料,了解背后的设计考虑。)
这个问题困扰了我们半个小时,考虑过两种绕过去的方法。
一是在封装Guice的Factory中,对对象实例进行处理,如果对象有init()方法,就执行一下。这种方法并不漂亮,涉及泛型、反射,有点复杂。
二是在应用代码中每次请求PersistentLayer时,调init()方法,设置一个静态开关变量,记录是否已经初始化过。同样,这种方法不漂亮,涉及了很多重复。将一个隐含的假定(使用PersistentLayer时,必须先调用init()方法)扩散到了应用代码的各个地方,这种重复是不能容忍的。
最后找到了一个解决方案,即使用Guice的“绑定到实例”的能力。在MyModule.class中,
PersistentLayer pl = new PersistentLayerMemImpl();
pl.init();
binder.bind(PersistentLayer .class).toInstance(pl);
网上有人提到“Guice目前来说还只是个玩具”,提出如下问题:
“不可否认Guice是一个设计精巧的IoC容器,速度也很快,但是这个容器连最基本的对象生 命周期管理都没有,也就是要提供JSR-250里面javax.annotation.PostConstruct和PreDestroy的功能,有人提 出用无参数的@Inject方法来模拟init,这样明显是对Annotation的滥用,用构造参数注入然后在构造方法里面做初始化,这虽然是一种解决 方法,但是有的情况下对象可能需要reinitialize,这样还是需要把初始化单独做为一个方法,用finalize方法来做destroy更不可 取,没法控制什么时候执行.这个问题去年三月就有人提出来了到现在还没有解决,Guice的开发者还是不够活跃,这里这么多人捧Guice,不知道有多少人正式用在项目中.”
我想这种方法也许是一种解决方案。
2008年11月20日星期四
订阅:
博文 (Atom)
