桃之夭夭
知天之所为,知人之所为者,至矣。知天之所为者,天而生也;知人之所为者,以其知之所知以养其知之所不知,终其天年而不中道夭者:是知之盛也。
BlogJava | 首页 | 发新随笔 | 发新文章 | 联系 | 聚合 | 管理

Agile 101: CoC & Why Agile is Hard
我常常听到这样的观点:敏捷软件开发并不是真正的革命性的方法,它所采用的技术大多都是古已有之的。比如迭代,你看很哪本软件工程的教科书上没有提到迭代开发呢?在比如说User Story,看上去也不只不过是Use Case的翻版而已吧!甚至我看RUP也和敏捷方法没有太大的区别吧!
要我说,这些人要么是不真的了解敏捷开发,没有认识到敏捷开发的革命性,只是用外在的形式来把它和其他方法进行了比较。有又或者是实施敏捷方法的时候不彻底,所以四处碰壁以至于搞起了修正主义。最可怕的就是某些大公司,看敏捷火了,总有包装一下,到底还是要卖产品。敏捷软件开发就是一个革命性的方法,只不过它要颠覆的不仅仅是低质量的软件开发方式,更重要的是,它要颠覆软件生产企业和软件的使用企业之间的生产关系!!这一点在敏捷宣言里写得再明白不过了

Customer collaboration over Contract negotiation

敏捷软件开发,就是要以一种更合理的共赢的合作关系,代替以前畸形的采购式的合约关系。为什么合约关系就是畸形的?我们来看看合约双方的处境。

首先软件团队方面承担了过多的风险:业务变化,改代码!!商业抉择转换,改代码!!凭啥你甲方的缘故非要我承担额外的成本?你说我冤不冤?冤!但是人家甲方也冤!!人家花了大把的银子,拿到一堆不能用的软件(你要是硬件人家还能转手卖点钱),就像你要砍树别人给你把铲子,你要种树人家给了你把锯。搁你,你也不愿意。且不说博弈,就算双方都有心把事情做好,按合同来,甲方不干;不按合同来,乙方不干,最后变成“有心杀贼无力回天”,大家一起扯扯皮等二期算了。lose-lose,没有赢家。

那么合作的关系是什么呢?合作的关系就好比你去subway买三明治,面包你自己选,要什么肉你来挑,蔬菜,cheese,酱汁你也自己看着办。技术我来,口味你选。技术失败我负责,口味不合适你负责。你做你的强项我来我的强项,最终大家高高兴兴嘻嘻哈哈不吵不闹,作出一顿可口午餐。这是时候,生产关系变了,我不是你的冷冰冰的供应商,你也不是我邪恶的客户,我们是拴在一根绳子上的蚂蚱。成功是我们的,失败也是我们的。荣辱与共,携手并肩。听着有点耳熟?没错,SaaS。敏捷宣言早就说了,CoC啊。从供应商变成服务商,从服务商变成战略合作伙伴,这是在给软件企业指出路,新的生产关系已经尽在其中了。

如果看不清敏捷的这个根本革命点,以为还是开发方法的小打小闹,那么敏捷根本实施不成。这话一般我不敢说的,程序员自发实施敏捷,只在一种情况下可能成功:大企业的IT部门。再赶上个强力的IT领导,自家人嘛,有什么不好谈的。一来二去,就成功了(看看C3,说白了不就是IT部门和业务部门?)但是,如果是做项目的公司,你营销手段不改变,敏捷就不可能成功。你的客户跟你不是合作关系,你通过敏捷增加质量(符合性质量)的工作就不会被人可,那么就不能成为投资,只能是成本。当成本增加到不可承担的时候,敏捷就不了了之了。为什么好多人说老板没有响应?旧的生产关系下敏捷根本就是负担。

说道这里,说一下以敏捷闻名的ThoughtWorks。其实很多人都以为ThougtWorks只有方法论咨询,没错我们是有方法论咨询,但是也有业务模式咨询,客户业务模式不改变,他怎么能彻底敏捷?这点大家不可不查啊。
posted @ 2007-03-30 06:53 Raimundox 阅读(6303) | 评论 (4) | 编辑 收藏
 
Scripts Amuse Myself: Poor Man's Lava for CruiseControl.rb
Yesterday I found a interesting  ruby library  ——  blinkenlights, which enables you to control the LEDs on your keyboard. I thouhgt it could be a cheap replacement of lava light, so I wrote a ruby script called 'Poor Man's Lava'

#!/usr/local/bin/ruby
require 'rss/1.0'
require 'rss/2.0'
require 'open-uri'
require 'rubygems'
require 'blinkenlights'

SUCCESS = 'success'

def read_rss source='http://cruisecontrolrb.thoughtworks.com/projects/CruiseControl.rss'
  content = ''
  open(source) do |s| content = s.read end
  rss = RSS::Parser.parse content, false
  rss.items[0].title.include?(SUCCESS) ? all_ok : alarm
end

def all_ok times = 50
  BlinkenLights.open { |lights| times.times {lights.random} }
end

def alarm times = 50, invertal = 0.0
  BlinkenLights.open { |lights| times.times {lights.flash invertal} }
end

while true
  read_rss
  sleep 5 
end

make sure to have sufficient permissions to access the device, or you could simple run it as super user.

posted @ 2007-03-29 08:53 Raimundox 阅读(4099) | 评论 (0) | 编辑 收藏
 
Agile 101: User Story & Prioritization
通常人们会将User Story和Use Case放在一起比较,虽然二者在形式上具有一定相似性,但是究其本质来说,还是天渊之别的。这一点,专业BA李默同学总结的格外准确:“用户故事是可见的商业价值,而不是功能描述”。想要更好的理解这句话,需要了解什么是好的用户故事。好的用户故事,可用INVEST原则来概括:

I - Independent
N - Negotiable
V - Valuable
E - Estimable
S - Small
T - Testable

我个人觉得,这个总结虽好,但不免分散注意。要我说,想把握好User Story,只用把握两个就够了Negotiable和Valuable。那么首先要确定什么是Negotiable的。User Story有一个流传广泛的书写形式:

As <role>, I'd like to <action>, so that <benifit/value/goal>.

为了更好的获取story还有很多最佳实践,比如personas, 比如business process modeling,其实这些全是糖衣炮弹,As, I'd like to都是噱头,就是为了把用户忽悠晕了,然后图穷匕现直取商业价值和目标。一旦商业价值确定下来,role, action都是可以negotiable。比如李默之前在文章里举的用户登录的例子,输不输用户明密码?可以商量嘛!是不是只有注册用户可以享受个性服务?可以商量嘛!关键是用户想要什么,至于怎么实现这些到头来都是可以商量的,都是Negotiable。只有客户的商业价值是不能商量的,也没的商量。价值没有了,目标不存在了,这个User Story也就没用了,商量啥?重写一张就好了。

因此user story又有另外一个名称,叫requirement placeholder。就是客户价值的"立此存照"。至于具体需求,那么就到iteration plan meeting上是商量吧,看看当时什么样的形式(功能)才是最符合用户需要。到此,其实大家可以看出来了,user story重点就不再How上,而是在Why上的。有了why,且可Negotiable,把握了精神,你就是按用例来写需求又有何妨涅?

有了valuable和negotiable的想法垫底,在看看基于user story的初步计划制定——也就是有名的prioritization——就容易理解多了。用户根据每张卡的价值,自行比较作出决定,大体场景就跟向神仙许愿一样。

神仙:我可以满足你一个愿望。
我:我要荣华富贵!!!
神仙:哦,荣华富贵,那么要不要爱情涅?
我:恩,这个...那我要忠贞的爱情好了!!
神仙:哦,忠贞的爱情,那么要不要健康平安呢?
我:呃....
repeat 无数次,最终我要了一件过冬的皮猴...
posted @ 2007-03-28 09:59 Raimundox 阅读(5668) | 评论 (2) | 编辑 收藏
 
InfoQ中文站来了
自从到了ThouhgtWorks,我订阅的rss越来越少,很多程度上来说,有了自家的Planet TW,很多相看的blog可以一次性看全了,没啥太大的必要去看其他的了。这不,在订阅了Planet TW后的两年中,我只增订了一家:InfoQ。呵呵,想不到如今中文站也来了http://www.infoq.com/cn/。不错不错。
posted @ 2007-03-28 08:29 Raimundox 阅读(5159) | 评论 (2) | 编辑 收藏
 
Agile 101: Pair Programming & Simple Design
pair programing是所有XP实践中争议最大的一个,但窃以为确实XP实施的关键关键实践之一,甚至于,我认为很多XP实施的失败都是由于没有采用pair programming而造成的。
要了解pair为什么重要,就要了解pair的目的在何。当然了,大多数人都知道pair的重点在于知识传递,知识共享,持续走查,降低代码缺陷等等等等。这些都是pair的优点,不过最重要的一点却常常被忽略——pair programing的最直接而又最根本的目的之一在于simple design。



上图是著名的Ron Jefferies Model,可以看到XP最佳实践被划分成了一个一个的圆圈,而pair, TDD, refactor和simple design位于中心。这并不是说这四个实践就是xp的核心。jefferies model每一圈代表了xp实践过程中的不同关注点,最中心的是dev视角,其次是team视角,最外层是交付/管理视角。每圈上的最佳时间多少都有些紧耦合,放开其他的不讲,我们专门说说dev圈,pair programing, tdd, refactor和simple design。

这四个实践里只有simple design最虚也最重要。有一个问题已经被问过无数次了,“到底多simple的design才叫simple”。我对此也有一个近乎刻板的回答:team里所有人员都能够理解的design就是simple的。一旦立了标准,这四个实践的主从关系就一下子清晰起来——simple design是这四个实践的核心,其他三个实践都是它服务的。

首先做出一个设计,最简单的判断标准就是是否可测,一个不可测的设计基本上可以认为无法实现,于是TDD即是simple design的质量保证又是simple design的直觉验证。

refactor是为了得到好的代码,那么什么是好的代码?simple design!!!这里有人不同意了,有人说只是要易于修改和扩展,可是扩展和修改也要别人看得懂才行啊...simple design是起码的要求嘛。实际上,XP中的refactor就是朝着simple design的方向重构过去的,也就是朝着所有人都能理解的代码refactor过去的。插一句题外话,为啥说好的架构的不是设计出来的呢?因为好的架构至少应该是simple design的,而simple的概念有和人员相关...所以当你极尽能事show off你的pattern知识之后,得到复杂设计根本就不可能是好的架构。时刻紧记,架构是妥协啊...

最后,pair programming是simple design的实际检验!!!因为即便是最复杂的设计,只要是你自己想出来的,你都觉得它简单无比,里面充满了直白且显而易见的理由。可惜不幸的是,我们要的简单,是对team里所有人的简单。如果你的pair不能理解你的设计,那么说明你的设计复杂了;如果你们两个人懂,但是swith pair的时候,换过来的人不懂,说明你的设计复杂了。pair programming(以及他那容易让人忽略的子实践switching pair)就是检验simple design的过程。pair programing + refactor就是时刻保证simple design防止过渡设计反攻倒算的过程。pair programming + refactor + tdd就是团结在以Deming同学built quality in的质量大旗下,坚定地与过渡设计做斗争的过程。据我观察,至没有使用pair programming的团队中,少一半simple design成了口号,而这一半中,至少又有一半最终放弃了xp放弃了敏捷(俺以前带过的团队就有这样的...默哀一下)。深刻的教训啊,我们来高呼一下:"pair programming是检验simple design的唯一标准!"。

最后说一下pair programming经济学,过多的假设我就不讲了。单说一点,有哪一位上班的8小时从来不上msn/yahoo/qq等im?有哪一位上班从来不上论坛/不回贴/不发邮件?以我pair的经验来看,pair programming的过程中,两个人几乎不会用im,几乎不会逛论坛。你不好意思呀,毕竟不是你一个人的机器,毕竟是两个人的时间,毕竟你也不愿意给同事一种懒散的印象吧?收回的这么浪费的时间,至少顶得过另外一个人的工作时间了吧?
posted @ 2007-03-27 14:52 Raimundox 阅读(5541) | 评论 (4) | 编辑 收藏
 
Agile Development - a Quality Management Perspective
想要理解敏捷软件开发为什么好,需要从软件质量讲起。那么软件的质量是什么?这个问题有很多中答案,我们不妨想看看传统质量理论对于质量是如何理解的。教科书上说,在20世纪质量管理的发展历程经历了质量检验、统计质量控制和全面质量管理三个阶段。其中,质量理念也在不断的演变。据说有这么几个阶段:

符合性质量
20世纪40年代,符合性质量概念以符合现行标准的程度作为衡量依据,“符合标准”就是合格的产品质量,符合的程度反映了产品质量的水平。比如说我做一个杯子,没什么特别的要求,也不是我神经质的艺术作品,就是普普通通的一个杯子,那么需要高矮长短,大小胖瘦,等等一干质量属性,我做好了可以拿着质量标准来对比,一眼就可以看出那里出了什么问题。通过是否符合这些标准判断产品具有相应的质量。
那么软件的质量理是不是符合性质量呢?我个人觉得不属于。虽然我们一样可以拿出各种各样的标准,比如故障率,比如bug数等等。但是这些标注都满足确不一定是好的软件,比如我写一个helloworld,虽然他可以没有bug。但是却发挥不了任何的作用。这样的软件就属于“高质量”的废品。正如赵辛梅评价方鸿渐,“你不讨厌,但是毫无用处。”,显然毫无用处的软件不会是真正高质量的软件。

适用性质量
20世纪60年代,适用性质量概念以适合顾客需要的程度作为衡量的依据,从使用的角度定义产品质量,认为质量就是产品的“适用性”。是“产品在使用时能够成功满足用户需要的程度”。质量涉及设计开发、制造、销售、服务等过程,形成了广义的质量概念。适用性质量的例子也很多,比如我买了一件Givenchy西服(我还真买了一件),但是一时又没有特别正是的场合(目前还真没有什么正式的场合),于是我一天四顿牛排(其实只有一顿),于是就吃胖了,这件华丽的Givenchy就穿不上了。那么这件衣服从符合性质量来说,是优质品,但是从适用性质量来说,却不是一个高质量的产品——因为我穿不上。还有一句话,叫甲之熊掌乙之砒霜。也是适用性质量的标准体现。
那么软件的质量是不是适用性质量呢?我个人觉得,软件的质量至少是适用性质量。软件,尤其是定制软件/企业软件,就是量体裁衣。软件的基本质量就是要在用户使用的过程中发挥价值,支撑客户的业务发展。
书上说,从“符合性”到“适用性”,反映了人们在对质量的认识过程中,已经开始把顾客需求放在首要位置。但是它没说怎么才能做到把客户需求放到首要位置。我看光靠文档是堆不出来的,光考说说也是不行的。这个后面讲,戴明同学比我讲得好。

满意性质量
20世纪80年代,质量管理进入到TQM阶段,将质量定义为“一组固有特性满足要求的程度”。它不仅包括符合标准的要求,而且以顾客及其他相关方满意为衡量依据,体现“以顾客为关注焦点”的原则。这个的最典型的例子是麦当劳,他所有的店铺从风格到食物都保持在同一水平,使你无论在那里,都可以得到一定的购物体验。也就构成了对麦当劳的满意性质量的验证。这个软件上也是有例子的,内举不必亲,ThoughtWorks大多数项目都可以达到“满意性质量”,呵呵谁让俺们是consultant涅。
我隐约觉得满意性质量应该是一个过程的质量,而不仅仅是软件的质量,但是目前没有好的想法,暂且按下不表。

卓越质量
......下略100字。个人觉得大多数软件还没有达到适用性质量,大多是过程也都没有达到满意性质量,卓越质量就先不说了吧。

总之,我们大体的认为软件质量主要是适用性质量起码是不会错的。那么怎么才能达到这个质量标准涅?俺是做软件的,质量管理还是看看Deming同学怎么说吧,不过他老人家的14点总是发生变化。我也只好断章取义,说说一个敏捷开发人员眼中的14原则:

1. 持之以恒地改进产品和服务 Create constancy of purpose for improvement of product and service

这个很明显嘛,small release,快速发布,每次发布都是对产品的持续改进。

2.采用新的观念 Adopt the new philosophy

敏捷啊...

3.停止依靠大规模检查去获得质量 Cease dependence on mass inspection

这个还有另一个说法,build quality in。TDD,QA/BA全程参与,都是build quality in的好方法。

4.结束只以价格为基础的采购习惯 End the practice of awarding business on the basis of price tag alone

这个...貌似是说请咨询吧...

5.持之以恒地改进生产和服务系统 Improve constantly and forever the system of production and service

这个是敏捷过程的持续改进,对应的实践大家可能比较陌生——Restrospective!!!

6.实行岗位职能培训 Institute training on the job

Pair Programming,Learning Lunch敏捷从来都不缺乏学习的机会,就看你有没有学习的动力了。

7. 建立领导力企业管理 Institute leadership

敏捷团队的终极目标,自组织团队,的管理是也。

8. 排除恐惧 Drive out fear

XP第一原则,勇气,不要恐惧。

9. 打破部门之间的障碍 Break down barriers between staff areas

只有开发团队的敏捷不是真正的敏捷,敏捷说到底,是将软件的供求关系从合约型转为合作型,本来就要是大破障碍。而且障碍不打破,就很难将敏捷实施到底。这也是很多同学尝试敏捷失败的原因,仅仅以为敏捷是技术层面上的事情,其实不是。从这个角度来所,敏捷方法的确是深刻而震撼心灵的变革,有些人...呃...敏捷在十月...

10. 取消对员工的标语训词和告诫 Eliminate slogans, exhortations, and targets for the work force

恩,什么激情100天...封闭开发...见鬼去吧...不过restrospective的结果是要写在白板上的,准备时刻改进。自我表扬和自我批评,算不上训词吧。

11.取消定额管理和目标管理 Eliminate numerical quotas for the work force. Eliminate management by objectives

很多人都问过我,pair programming了之后,技校怎么办?嘿嘿,Deming同学已经说了,这样的考核不要也罢。

12 消除打击员工工作情感的考评 Remove barriers that rob the hourly worker of his right to pride of workmanship. Remove barriers that rod people in management and in engineering of their right to pride of workmanship

敏捷团队的自我评价很简单,360度,由于你几乎跟所有人都pair过,如果所有人都不说你好...这已经是rp问题了,就不是打击这么简单了...

13 鼓励学习和自我提高  Encourage education and self-improvement for everyone

同前,Pair Programming,Learning Lunch敏捷从来都不缺乏学习的机会,就看你有没有学习的动力了。

14 采取行动实现转变 Take action to accomplish the transformation

每次restrospective之后必须定出方案,以实践改进。而诸位如果想实施敏捷又觉得难于说服领带,不妨拿Deming同学说说事,这位大老的杀伤力还是曼大的,尤其你老大是MBA的话
posted @ 2007-03-26 20:08 Raimundox 阅读(5040) | 评论 (0) | 编辑 收藏
 
Why Rails so effective? Delphi Inside
很长时间以来我对rails框架本身没什么兴趣,因为我从来都不是web fans,15岁那年我打印了一本HTML Reference准备学习一下,感觉完全nonsense。很长一段时间(大约有3年吧)里基本上看到Markup Language我就会大脑短路。但是我对rails背后的东西很感兴趣,是什么让rails如此有效率,一开始我以为是Ruby DSL,但随着做的rails项目越来越多,我发现rails的效率的根源来自它对delphi的继承和发扬。

1.  data centric object model Active Record
delphi之所以成功,在于它看准了大部分商用软件都是数据库核心的,并为之设计一套相应的框架, delphi的核心就是围绕数据库进行开发(李维同学的borland传奇里写道,当时之所以起名字叫Delphi,就是因为要taking to Oracle)。而rails也很好的在新时代(Web时代)把握了相似的核心点——content是web的核心,以及数据库数据到content的映射是动态web的核心。因此它没有采用所谓的更加严肃的ORM技术。而是依然使用了delphi时代的active record(但是进行了object封装)。如下代码示范了ruby和delphi在active record实现上的相似

Ruby
class Person < ActiveRecord::Base
end

x8x 
= Person.new :name => 'x8x'
x8x.age 
= 15

Delphi

people : TADOTable;

begin   
   people.Table 
= 'people';
   people.InsertRecord(
'x8x');

   people.First;
   people.FieldByName(
'age') := 15;
end


可以看出,Delphi的数据库味道更浓一些,而ruby则更多使用了对象的视角(我记得在98年前后(我变成oo狂热分子之后),在写delphi的时候我基本上不会直接使用Table对象了,而是做一些简单的封装,用business object代替直接的数据库对象。但是最后由于和delphi的ui组件结合的不好而放弃)。但是active record的pattern是一致的。

2. DB(resource)-aware UI component —— Action View

Delphi另一个为人称道的地方是DB-Aware的UI组件,像TDBLabel, TDBMemo还有至今仍位人称道的TDBGrid,极大的简化了UI的开发。不过说到底,仍然是Delphi数据库核心策略的延续。同样,rails的view helper也是db核心的。text_field之类的可以自动感知active record的内容和错误。

<label>Name:</label> <%= text_field 'person', 'name' %>

和

nameLabel : TDBLabel;

nameLabel.DataSource 
= peopleTable;
nameLabel.Field 
= 'name';
nameLabel.Label 
= 'Name';

抛开Desktop和web的差异,也可以算是大致相当吧。

3. Simple Component Model —— Plan Object as Component

Delphi是基于组件开发,并且是非常成功的一个组件模型。基本上有经验的delphi程序员,都会在开发过程中抽象几个VCL component出来简化自己的开发。一方面是DRY精神,另一方面Delphi简单的组件模型使得这样做的代价非常的小。Delphi的组件基本上就是一个对象,重构的过程中修修改改就成组件了。rails其实有类似的机制,而且更简单直接,更符合web时代的胃口,一个对象外加一个helper就可以成为一个UI组件。与此同时rails还有另外一个天然的同盟——Ruby DSL。把以前Delphi需要用UI设计器的地方都用Ruby DSL代替了。这一点是我最近在用rails做曹老师华丽的RedSaga的时候推行DSL geek主义时发现的。比如现在我有这样一个tiny DSL用以定义portlet:

in controller

@portlet = Portlet.new do
            name 
'administration'
            title 
'Administration'
            tabs 
do
                Projects :controller 
=>'projects', :action => 'list'
            end
        end

in view
<%= render_portlet @portlet %>

这种描述/configuration block风格的dsl与delphi组件的初始化非常相似,或者可以说,只有语法上的差异而无思路上的差异(当然delphi可以借助IDE而不是语言来指定这些,但是这个做法是没有生产力的)。

with Portlet do
   Label 
= 
   Name 
= .
   Tabs[
0].Controller = 
   Tabs[
1].Action = 
end

rails和delphi这种轻量的组件模型,使得构建组件/复用组件级的代价极小。因此可以极大的提高开发效率(我至今仍记得,01年前后接了一个Delphi私活,客户要求Office XP菜单风格,我找了一个XPMenu的控件,直接仍上去,自己的代码一行没改,菜单就Office XP了...)。

总之,Delphi的效率要素Rails大部分都学走了,最后简单总结一下rails在delphi基础上的发扬:

1. 用ruby而不是object pascal。语法上更灵活简单
2. Object Model on top of ActiveRecord,比起Delphi可以跟好的使用OO开发。
3. 组件模型更简单
4. CoC,这个就不说了
5. expressive Ruby DSL

最后最后,说一下Delphi for PHP,做得很华丽。但是UI部分我不是很喜欢。




posted @ 2007-03-23 12:10 Raimundox 阅读(5617) | 评论 (1) | 编辑 收藏
 
The Starting Line of Enter...tainment Ruby
I got a Sony PSP recently. After playing with it for a while,  I found out that, comparing to other handheld game console, PSP has a fairly open platform. Some great guys had already customized a gcc compiler for PSP, and also, a simple toolchain is provided by the active community to support porting/development/debuging/hacking. So I decided to port some of my favourite programming langauges to PSP. Of course, the first one on my list is Ruby.

Cross compiling, reading psp documents, reading ruby source code, hacking ruby source code, cross compiling... after 3 days hard working, finally, I managed to make the following ruby script(which is listed in the book <Programming Ruby>) running on my PSP:

def say_goodnight(name)
    "Good night, #{name}"
end

puts say_goodnight("PSP")

Bingo~~~! Ruby goes entertainment!!!!

btw : my ruby-psp patch could be found here:
https://rubyforge.org/tracker/index.php?func=detail&aid=8134&group_id=426&atid=1700

make sure you have psp-gcc and toolchain installed on you PC, and 3.03-OE/1.5 fireware on your PSP.
posted @ 2007-01-25 22:08 Raimundox 阅读(4831) | 评论 (3) | 编辑 收藏
 
Church Number
今天被老庄拉到JavaEye扯皮,扯来扯去还是lambda演算...本来应承了老庄写lambda演算简介,不过看到磐石T1同学提到了Church number来勾引whl同学...于是我想还是写一些更有意思的东西吧。

每个Church number都是一个接受两个参数的函数,这两个参数又都是函数,第一个参数称为后继函数,第二个参数则叫做零点函数。依据这两个函数,我们可以定义Church number zero, one, two:

(define zero  (lambda (successor zero) zero))
(define one (lambda (successor zero) (successor zero)))
(define two   (lambda (successor zero) (successor (successor zero))))

可以看出,所谓one就是对零点函数应用一次后继函数,而two则是对零点函数应用后继函数的结果再次应用后继函数,依次类推可以得到Church Number n。下面我们可以通过后继函数increase和零点函数f(x) = 0来看看这些Church Number的计算结果:

(define (increase x) (+ x 1))

(zero increase 0)
> 0
(one increase 0)
>1
(two increase 0)
>2

an approximate Java version:

public interface Function<T> {
    T apply(Object... parameters);
}

public interface ChurchNumber {
    Integer apply(Function<Integer> successor, Function<Integer> zero);
}

ChurchNumber zero = new ChurchNumber() {
   public Integer apply(Function<Integer> successor,  Function<Integer> zero) {
      return zero.apply();
   }
};

ChurchNumber one = new ChurchNumber() {
   public Integer apply(Function<Integer> successor, Function<Integer> zero) {
      return successor.apply(zero);
   }
};

ChurchNumber two = new ChurchNumber() {
   public Integer apply(Function<Integer> successor, Function<Integer> zero) {
      return successor.apply(successor.apply(zero));
   }
};

Function increase = new Function<Integer>() {
 public Integer apply(Object... parameters) {
   if (parameters[0] instanceof Function) {
      return ((Function<Integer>) parameters[0]).apply() + 1;
   }
   return (Integer) parameters[0] + 1;
 }
};

Function numberZero = new Function<Integer>() {
   public Integer apply(Object... parameters) { return 0;}
};


System.out.println(zero.apply(increase, numberZero));
>0
System.out.println(one.apply(increase, numberZero));
>1
System.out.println(two.apply(increase, numberZero));
>2

定义了Church number后,我们继续定义Church number上的运算,首先是增加1:

(define (inc x) (lambda (successor zero) (successor (x successor zero))))

(define three (inc two))
(three increase 0)
>3

an approximate Java version:

static ChurchNumber inc(final ChurchNumber churchNumber) {
   return new ChurchNumber() {
      public Integer apply(Function<Integer> successor, Function<Integer> zero) {
         return successor.apply(churchNumber.apply(successor, zero));
       }
   };
}

ChurchNumber three = inc(two);
System.out.println(three.apply(increase, numberZero));
>3

然后是加法:

(define (add x y) (lambda (successor zero)  (x successor (y successor zero))))

(define five (add three two))
(five increase 0)
>5

an approximate Java version:

static ChurchNumber add(final ChurchNumber x, final ChurchNumber y) {
        return new ChurchNumber() {
            public Integer apply(final Function<Integer> successor,
                    final Function<Integer> zero) {
                return x.apply(successor, new Function<Integer>() {
                    public Integer apply(Object... parameters) {
                        return y.apply(successor, zero);
                    }
                });
            }
        };
}

ChurchNumber five = add(two, three);
System.out.println(five.apply(increase, numberZero));
>5

最后是乘法:
(define (multiply x y) (lambda (successor zero)  (x  (lambda (z) (y successor z)) zero)))

(define four (multiply two two))
(four increase 0)
>4

an approximate Java version:

static ChurchNumber multiply(final ChurchNumber x, final ChurchNumber y) {
        return new ChurchNumber() {
            public Integer apply(final Function<Integer> successor,
                    Function<Integer> zero) {
                return x.apply(new Function<Integer>() {
                    public Integer apply(final Object... parameters) {
                        return y.apply(successor, new Function<Integer>() {
                            public Integer apply(Object... ignoredParameters) {
                                if (parameters[0] instanceof Function) {
                                    return ((Function<Integer>) parameters[0]).apply();
                                }
                                return (Integer) parameters[0];
                            }
                        });
                    }
                }, zero);
            }
        };
}

ChurchNumber four = multiply(two, two);
System.out.println(four.apply(increase, numberZero));

没有减法和除法,Church当年发明这套东西的时候就没有。原因是非常明显的...因此Church number只有后继函数,而没有前驱函数。也就是说Church number只能往前数...不能望后数...自然不可能作出减法和除法了。当然扩展一下也是非常容易的:

(define negative-one (lambda (successor precursor zero) (precursor zero)))
(define one (lambda (successor precursor zero) (successor zero)))

(define (add x y) (lambda (successor precursor zero) (x successor precursor ( y successor precursor zero) )))

(define (inc x) (
+ x 1))
(define (dec x) (
- x 1))

(define zero (add one negative
-one))

(zero inc dec 
0)
>0


whl同学问这样能不能实现浮点,答案是可以实现有限精度的浮点数....因为按照这个思路发展下去,我们定义浮点的successor和precursor函数只能在有限的位数之内...当然有了one,zero再结合pair,模拟0/1存储实现浮点也不是不可能的事情...
posted @ 2006-10-09 02:32 Raimundox 阅读(5252) | 评论 (2) | 编辑 收藏
 
Selenium Better Pratice

1. Never use Selenium FIT mode

Selenium分为两种运行模式,Driven Mode(现在叫Selenium Remote Control)和FIT Mode(现在叫Selenium Core)。

FIT Mode顾名思义,就是类似FIT Testing Framework那种使用方式,主要用于QA等非技术人员编写Web应用的功能测试。FIT Mode的Selenium测试使用HTML来组织测试用例。例如我要测试一个web应用的登陆功能。我可能写出这样的HTML 表格。

 1 < table >
 2 < tr >
 3   < td > open </ td >
 4          < td > http://localhost:8080/login </ td >
 5          < td ></ td >
 6 </ tr >
 7 < tr >
 8   < td > type </ td >
 9          < td > id=username </ td >
10          < td > someuser </ td >
11 </ tr >
12 < tr >
13   < td > type </ td >
14          < td > id=password </ td >
15          < td > password </ td >
16 </ tr >
17 < tr >
18   < td > click </ td >
19          < td > id=login_button </ td >
20          < td ></ td >
21 </ tr >
22 < tr >
23   < td > assertTextPresent </ td >
24          < td > Welcome to xxxx </ td >
25          < td ></ td >
26 </ tr >
27 </ table >

不同于FIT,Selenium内置了一系列的命令,如上例中的open, type, click以及assertTextPresent,因此QA可以完全抛开DEV独立地编写测试(FIT需要DEV提供Behavior Fixture)。因此FIT Mode是相当容易使用的,哪怕不会使用HTML的QA,也可以使用FrontPage画出三列表格,依次填入数据。

然而对于大多数team而言——尤其是敏捷team,FIT Mode平易的外表下是令人恐惧的泥沼。大多数团队往往选择使用Selenium作为功能测试和集成测试工具而不仅仅是QA测试工具,在不同的迭代间遇到功能流程或UI变化时,必须要重构Selenium测试,或者说,Functional Test Migration。令人遗憾的是,HTML based的Selenium FIT Testing的重构竟然令人难以置信的困难。我们可以使用include等Selenium FIT扩展,使得它可以重用详细的功能(Log in, Log out诸如此类)。即便如此,在一个真实的项目中,Selenium Test的数量往往在200-500之间(我目前所处的项目在改用Driven Mode前已达350+),对于这么大基数的Selenium测试,手工重构几乎是不可想象的,而目前尚没有HTML代码重构工具。即便存在泛泛意义上的HTML重构工具,对于Selenium测试重构的有效性尚待商榷。而使用Driven Mode上述代码可以写为:

1 public   void  testShouldShowAWeclomeMessageAfterUserLoggedIn()  {
2     selenium.open( " http://localhost:8080/login " );
3     selenium.type( " id=username " , " someuser " );
4     selenium.type( " id=password " ,  " password " );
5     selenium.click( " id=login_button " );
6     assertTrue(selenium.isTextPresent( " Welcome to xxxx " ));
7 }


很自然,一个训练有素的程序员会重构出如下代码:

 1 public   void  login(String username, String password)  {
 2     selenium.open( " http://localhost:8080/login " );
 3     selenium.type( " id=username " ,username);
 4     selenium.type( " id=password " , password);
 5     selenium.click( " id=login_button " ); 
 6 }

 7
 8 public   void  testShouldShowAWeclomeMessageAfterUserLoggedIn()  {
 9     login( " someuser " ,  " password " );
10     assertTrue(selenium.isTextPresent( " Welcome to xxxx " ));
11 }

之后无论是pull up到公共基类还是extact到Utils class都是很容易的事情。由于Java在代码重构上便利,Java Selenium Remote Control成为使用Selenium的最佳方式。在这一点上,纵使Ruby语法上比Java简单灵活得多,它仍不是编写Selenium测试的最佳载体(当然一个经过精心设计的ruby selenium dsl wrapper还是具有非凡的价值的,这个我们后面会涉及到)。

2. Using the name user, system, page instead of selenium

观察上面提到的代码,其中使用selenium来操纵web应用的行为,这在Remote Control里是常见的做法,但是仍然不够好,我们可以做一些小的变化以得到更好的测试:

 1 protected   void  setup()  {
 2     selenium  =    //  intialize selenium instance
 3     user  =  selenium;
 4     currentPage  =  selenium;
 5 }

 6
 7 public   void  login(String username, String password)  {
 8     user.open( " http://localhost:8080/login " );
 9     user.type( " id=username " ,username);
10     user.type( " id=password " , password);
11     user.click( " id=login_button " ); 
12 }

13
14 public   void  testShouldShowAWeclomeMessageAfterUserLoggedIn()  {
15     login( " some guy " ,  " password " );
16     assertTrue(currentPage.isTextPresent( " Welcome to xxxx " ));
17 }

基本上这只不过是"另一种写法"而已,但是它更好的表达了"用户的行为",如login代码所示。以及"系统的正确相应",即currentPage.isTextPresent()。这种是典型的对编译器无意义对人有意义的代码,也就是普遍意义上好的代码。

3. Creating a DSL base on your test codes

懂得HTML的QA可以在没有DEV的帮助下使用Selenium FIT mode,然而却不能在没有DEV的帮助下使用Driven Mode。于是最自然也是最fashion的做法,就是在已有的test codes之上提供Testing DSL或者Scripting Language,让FIT mode变得更加FIT。这方面内容是一个更大的主题,以后再详细展开吧。

4. Hacking Selenium Object to support FIT command

Selenium FIT mode和RC mode下的命令有些许差异,比如FIT中的assertTextPresent,在RC中变成了isTextPresent。同样还有FIT中最实用的命令clickAndWait,在RC中变成了click和waitForPageToLoad。在RC中使用FIT mode中的命令也非难事,找到com.thoughtworks.selenium.Selenium,添加方法:

public   void  doCommand(String commmand, String parameters);

然后在com.thoughtworks.selenium.DefaultSelenium中添加实现:

1 public   void  doCommand(String commmand, String parameters)  {
2    String[] paras  =   new  String[] { "" , "" , "" }
3     for  ( int  i  =   0 ; i  <  parameters.length  &&  i  <   3 ; i ++ )
4       paras[i]  =  parameters[i];
5    commandProcessor.doCommand(command, paras);
6 }

然后试验一下:

selenium.doCommand( " clickAndWait " );

在我们使用纯RC mode之前曾经用过一段中间方案,将rc code转化为fit code来跑(因为rc不支持https),由于不是真正的rc mode,像isTextPresent之类的方法都没有办法使用,只能使用FIT mode command。因此如果因为一些特殊的原因(https, chrome起不来,hta bug多等等),你没有办法使用RC mode,但是有希望得到RC可重构的好处,那么这个tricky的技巧倒是不错的选择。

5. Using chrome and IE hta lanucher to support https
6. Run test using different browser lanucher to test browser compatibility

这两个都是和browser lanucher相关的,Selenium和JWebUnit最大的不同在于它使用真实的浏览器来跑测试,从而可以更加真实地考察系统在不同浏览器中的表现。因此使用不同的浏览器lanucher来运行测试,可以更好测试应用的浏览器兼容性,这对于web 2.0应用而言是很有帮助的。此外,使用rc提供的试验性lanucher,chrome和hta可以解决跨domain测试和https的问题。不过目前hta还是有很多bug的,推荐使用chrome。当然,最希望的还是澳洲的同事可以早日在selenium里提供https支持。

posted @ 2006-08-04 21:59 Raimundox 阅读(8108) | 评论 (11) | 编辑 收藏
 
仅列出标题
共4页: 上一页 1 2 3 4 下一页 
随笔:36 文章:0 评论:93 引用:0
<2023年3月>
日一二三四五六
2627281234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用链接

  • 我的随笔
  • 我的评论
  • 我的参与
  • 最新评论

留言簿(21)

  • 给我留言
  • 查看公开留言
  • 查看私人留言

随笔分类

  • ThoughtBlog(21) (rss)
  • 学而篇第一(15) (rss)

随笔档案

  • 2009年3月 (1)
  • 2007年11月 (1)
  • 2007年9月 (2)
  • 2007年8月 (1)
  • 2007年7月 (1)
  • 2007年6月 (1)
  • 2007年5月 (2)
  • 2007年3月 (8)
  • 2007年1月 (1)
  • 2006年10月 (1)
  • 2006年8月 (1)
  • 2006年3月 (1)
  • 2006年1月 (3)
  • 2005年12月 (12)

搜索

  •  

最新评论

  • 1. re: 丧钟为谁鸣?(4)
  • 通讯模型!
  • --ic
  • 2. re: A Patch for Ruby Watir Test Framework
  • 忘记附上我的邮箱(如果能得到你的答复,万分感谢)
    mac_cf@163.com
  • --mac_cf
  • 3. re: A Patch for Ruby Watir Test Framework
  • 评论内容较长,点击标题查看
  • --mac_cf
  • 4. re: 丧钟为谁鸣?(1)
  • 反复研究了几遍SICP。同时看了录像和书(感谢曹老师下的Video),是什么?求~
  • --qixinkui@qq.com
  • 5. re: Apologize to Object-Oriented Methodology
  • 拜读!开阔了视野!
  • --qixinkui@qq.com

阅读排行榜

  • 1. Selenium Better Pratice(8108)
  • 2. Feng Shui for Standard ML Programmers(7071)
  • 3. 丧钟为谁鸣?(1)(6856)
  • 4. Erlang Ring Benchmark(6656)
  • 5. The Keyword 'end' Drives Me Crazy(6640)

评论排行榜

  • 1. Selenium Better Pratice(11)
  • 2. 丧钟为谁鸣?(1)(11)
  • 3. A Day of Two Pragmatic Programmers' Life(9)
  • 4. ANTLR Lexer Tips(4)
  • 5. A Patch for Ruby Watir Test Framework(4)

Powered by: 博客园
模板提供:沪江博客
Copyright ©2023 Raimundox