2009年2月26日星期四

转账Transaction(交易/事务):业务层和持久层设计

在邱郁惠的《系统分析师UML实务手册中》曾读到,Coad的彩色UML模型在早期曾是以事务为中心的。在Coad后来的著作中,仍可以看到事务的烙印:aMomentInterval.commit()。

巧的是,在Prevayler的实现中,强调的也是事务:它要求开发者设计可以序列化的“事务对象”,利用Java的序列化机制实现持久,而所有对象的目前状态则保持在内存中。

三年以前我开始看Prevayler时,觉得它的这种“事务对象”不好理解,别扭,不是我喜欢的东西。在读了彩色UML建模之后,我理解了这种持久设计。下面以银行账户转账的例子来说明我的理解(注意,这个例子是虚构的,不存在于任何真实的系统中。如有雷同,纯属巧合):

业务模型

关于这个业务模型,有两点说明:
  1. 按照“5Ws and an H”的方式设计。MoneyTransferring本身记录了转账的金额和时间,说明了这是个什么事(What),何时发生(When)。MeansDesc是转账方式描述,可能是通过ATM机、柜台、电话银行等方式完成这次转账,说明了发生方式(How)。Terminal说明这次交易在哪里发生(Where)。Staff和Customer说明了参与这次转账的人(Who)。转账的理由(Why)则因为没有很合适的分类方法,可能作为MoneyTransferring的一个字符串属性。
  2. 所有的类,都可以作为分类统计的依据。例如,我们可以根据一个账户的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项修炼

(本文已在《程序员》杂志2009年第2期发表)

Peter Coad是我喜欢的一位面向对象专家和软件创业者。他在上世纪九十年代与人合著了6本关于面向对象软件的分析、设计和编程的书籍,以《面向对象分析》一书中和Yourdon共创的Coad/Yourdon方法而成名。1999年,他创建了TogetherSoft公司。2003年,TogetherSoft卖给了Borland公司,他成为了Borland公司的副总裁。后来,他离开了Borland,兴趣转向了软件之外,教人读希腊文版的圣经。

他最值得一提的技术贡献,就是提出了“彩色
UML”建模方法,这也是他最后一本书《彩色UML建模》的主题。在这种建模方法中,他使用了4种颜色,代表4种不同设计意图的类,创造性地回答了面向对象分析和设计的一个根本问题:“怎样识别类,并确定方法和属性?”这本书已由机械工业出版社引进版权,出了中文版。

我对
Coad的关注始于十多年前,那时朋友向我推荐了北大出版社引进的《面向对象分析》一书。后来因为学习JavaUML建模、关注Borland公司的产品,又看到了TogetherSoftCoad的名字。再后来,敏捷方法学兴起,我又看到了Coad,他是特征驱动开发(FDD)的主要支持者。向师傅学习,要学师傅练功的方法,而不只是师傅的招式。我想知道的是:为什么Peter Coad会发明彩色UML的方法?或者说,Peter Coad是怎样炼成的?

在他
1994年左右的一篇文章中,对这个问题给出了一些提示。这篇文章标题是“Amplified Learning”,介绍了“7种智能”的理论和应用。

语言是第一种智能。
著名的通天塔的故事体现了自然语言表达的重要性:失去了语言沟通能力,团队协作就变成了不可能的任务。语言能力的习得,有一句话可以概括:“听说领先,读写跟上”。学习本国语言和外语都是如此。语言的重要性,实际上体现的是沟通的重要性。据说,当年
TogetherSoft公司在面试时,会先让被面试的人选一个与软件开发无关的话题讲15分钟。通过这样的面试环节,可以了解面试者对生活的一些理解和表达沟通的能力。这里我还想强调一下中国程序员的英语能力。由于软件方面的最好的信息几乎都是英语的,所以良好的英语能力对程序员有很大的帮助。一般来说,在各行各业,最成功的人都是拥有最好信息的人。

代码的可读性也是语言能力的体现。Knuth1984年提出的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’流水账和组件

在前面的贴子“彩色UML建模、SOA和《目标》”中,提到了‘5Ws and an H’流水账,这里进一步阐述一下。

流水账
  • When:这个业务事件何时发生?
  • Who:谁参与这个业务事件?
  • What:这是个什么事?涉及什么物品?
  • Where:这个业务事件发生在哪里?
  • Why:为什么会发生这个业务事件?
  • How:这个业务事件以何种方式发生?有无细节?

让我们来看一个例子:
特征:为客户打印发票(收据)
  • When:记录下“打印发票(收据)”这个业务事件发生的时间
  • Who:参与这个业务事件的人有两个:客户和业务员
  • What:这个业务事件是“打印发票(收据)”,涉及到一张纸质发票(收据),上面有序列号
  • Where:这个业务事件发生在公司旗下众多连锁店的一个里
  • Why:因为客户付了一笔款,客户付款是前驱业务事件
  • How:用户付款时享受了折扣优惠,需要在发票(收据)上注明

当我们把一个业务事件的这些方面都记录下来时,信息就完备了。值得一提的是,在具体业务事件中,上面的每个因素都可以有多个具体的值。例如,在持续集成中,因为前面有3次提交,然后在5分钟内没有新的提交,这触发了一次持续集成。那么,这次持续集成事件的前驱业务事件就有3个。

组件
这个业务事件构成了一个组件,它对使用的上下文做了一些假设。如果这些假设没有变化,这个组件就不需要改变,可以复用。

也可以把这个组件看成一个独立的数据源,它提供对这个数据源的数据分析统计功能。例如:
  • 这个月哪个业务员开出的发票最多?
  • 从上次领发票本到现在,某个连锁店开出了多少发票(是否要提醒领新的发票本)?
如果从流水账和总分类账的观点来看,可以认为“5W and an H”类似于“科目”,它们对流水账进行了分类。

组件粒度
组件粒度也就是业务事件的粒度。“进行一次销售”这个业务事件可能由“下订单”、“付款”、“打印发票”这3个小粒度的业务事件组成。 于是,我们可以在“进行一次销售”这个业务事件的“How”部分,标明它由3个明细业务事件构成。如果我们还允许一周内无条件退货,那么还可以包含“退货”的业务事件。

小粒度的组件组合起来,就构成了大粒度的组件,系统以这种方式体现伸缩性。这样做的好处在于:
  • 简单一致地伸缩,设计小系统和设计大系统的原则一样
  • 在不同的抽象层面上看系统,如操作层面和管理层面

总账会计师和CIO
总账会计师要能够从企业的总账中分析出企业的盈利能力和风险。CIO要能够从业务事件流水账中按“科目”进行整理,通过分析找到实现企业目标的风险和机会。

2009年2月8日星期日

RESTful的停车场图和开发Dashboard

“REST的原则包括:
  • 以资源为中心的方式。
  • 所有相关的资源都可以通过统一资源标识符(URI)来寻址。
  • 统一通过HTTP来访问:GET、POST、PUT和DELETE。
  • 内容类型协商允许从同一个URI取得不同表示形式。
  • REST风格的服务很容易通过运行在web浏览器、其他客户端或服务器上的代码来访问,这在Ajax的背景下很流行。
  • 完全利用WWW的缓存机制
  • 为同一资源的多种表示层提供服务。
简而言之,这些网络架构原则允许多个网站对用户的无缝展现。由于有了REST,因特网变成了一个动态的网络,而不只是点到点的连接,不只是基于远程方法调用(RPC)技术。”

以上内容引自正在译的新书“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日星期六

测试计划

关于制定test plan,我理解的是测试工作的进度安排。

最根本的一条原则:测试工作和开发工作结合得越紧密,效果就越好。

理想中的测试工作安排是这样的:
  • 在项目早期,测试团队的骨干参与需求工作,确保需求的可测试性,同时也帮助消除了需求的二义性。
  • 在概要设计/架构设计阶段,测试团队同步进行测试概要设计,确定测试的基础架构和基础设施。
  • 在详细设计/迭代开发阶段,测试团队同步进行测试用例设计和开发,确保开发团队通过持续集成交付的每个feature都在最快的时间内得到测试。容量测试、压力测试等也要在条件一成熟,就马上进行。

随着产品代码的不断增加,自动化测试的代码也不断增加。这些测试代码在整个软件生命周期中都有用。

测试成本低的的设计是好设计,测试成本高的设计是不好的设计。降低测试成本是降低软件总体拥有成本的根本途径。

人总是会犯错误。尽早发现,尽早更正,确保决不再重犯同样的错误,是我们的追求。

参考书籍:《快速测试》、《持续集成》、《Java测试新技术:TestNG和高级概念