-
2008-03-28
半路出家
不知道算是幸运,还是不幸,我参与的多数项目,如果不是从头开始,我也是最开始加入的几个人之一,所以,我通常对项目的来龙去脉都比较清楚。这次,我有机会尝试一下在一头雾水的情况下开始一个项目。
这个项目已经进行了超过一个月,因为客户需要在赶在最近的一次发布之前,增加一些功能。老板按照目前的进度估算了一下,如果不加人,这个任务很难完成,于是客户很合作的同意增加两个人。就这样,我成了临时工。事实上,在这个阶段进入,项目早就过了最初的阶段,远远望去,没有几百也有几十的程序文件屹立在那里。虽然有最熟悉项目的人为我们介绍了项目的背景和架构,但这些宏观层面上的东西,对于编写代码这样“微观”的操作而言,几乎是没有任何直接的帮助。作为一个新人加入到项目,除了发呆,我还能做什么呢?
回想在以前公司的情形,大多数新进项目的人多半是得到一大堆文档,然后,有人语重心长的说,先把文档看了吧!且不说这些文档这种东西几乎写出来就过时,单单读完这些文档就需要花费很多时间。运气好的话,还有代码可以对应,但千万不要指望你能够一下子读懂这些代码。也许,你会想找别人来问,如果不出意外,大多数人都会不明就里的非常忙,忙到有时间上网聊天却没有时间给你讲这些东西。事实上,很多人即便自己做了很多东西,也不一定能够清晰的描述出来,所以,即便找到一个“闲”人,成功从他那弄明白这些东西的概率也很低。其结果是,我们经常会看到一些人在那对着电脑上的文档发呆。
在ThoughtWorks,我也恨不得找到一堆这样的无聊文档来打发时间,以此来享受偷懒的时光。遗憾的是,完全没有这种机会。因为那些“文档”会被视为浪费,这在以消除浪费为己任的敏捷来看,是无论如何不能接受的。于是,我在找不到任何借口的情况下,进入项目的第一天,就开始写程序了。不了解项目,怎么写程序?这是个问题。
敏捷实践中,有一个叫做Pair Programming的,从字面上来看,就是两个人一起开发。对于ThoughtWorker们来说,Pair是一种常态。所以,我在开发时也会有一个Pair,虽然我对项目一头雾水,但我的Pair已经在这个项目上工作了很长一段时间,所以,他很清楚这个项目的一切,差不多一切,因为代码是集体所有,所以,他在开发过程中会接触到各个部分。
拿到我们要做的Story,我的Pair会结合这个Story给我介绍上下文,并结合代码大致描述一下我们要做的事情。虽然在这个项目上我是新手,但我并不是对编程一无所知。有了这些基本的信息,我至少对我们要做什么,以及如何来做,在心里已经形成了一个大致的印象。刚开始时,基本上是我的Pair在主导开发,一边做一边告诉我,我们已经走到了哪里。渐渐的,我已经对我们在做的代码有了一些认识,开发也开始由一个人主导转向两个人讨论。随着开发的深入,我也发现了现有做法的一些不足,于是,我提出对代码进行重构,并给出了自己的分析和建议。我的Pair在听了之后,认为这是一个可行的建议,于是,我们毫不留情的将那段大家看着不舒服的代码改掉,这段代码从此清净了。
这就是在加入项目前几天所做的事情,虽然我目前还不能对整个项目有个很好的把握,但是,我相信,我已经开始在这个项目中起作用了。我想,Pair Programming是主要原因。正如前面所说,虽然我对项目很无知,但我的Pair很好的弥补了我的不足。正是两个人的协调工作,让我可以在对项目没有完整认识的情况下,可以很快入门,以最快的速度融入到开发之中。曾几何时,我对Pair Programming的认识还停留在大家一起写程序和知识分享上,原来它对半路出家的人帮助也很大。虽然《人月神话》教导我们说,加人起不了很大的作用,但Pair的方式至少可以在相当大的程度上发挥新人的价值,削弱加人带来的负面影响。
-
2008-03-25
程序设计语言的表达——内部DSL
使用Java实现内部领域特定语言
One Lair and Twenty Ruby DSLs
Implementing an Internal DSL
上面几个文章都是关于DSL的,不过,在这里,我并不是太关心DSL的话题,我更感兴趣的是代码的写法。按照这几篇的分类方法,直接用程序设计语言编写的DSL算是内部DSL,也就是说,所谓内部DSL,也就是一种标准的程序代码。
Kent Beck在他的《Implementation Patterns》的第三章《A Theory Of Programming》中,谈到了编程的价值观(Value):Communication(沟通)、Simplicity(简单)和Flexibility(灵活)。如果说简单和灵活很容易理解的话,那么把沟通放在价值观中,尤其排在所有价值观的第一位,则显现出Kent Beck对于编程的深刻。在这个软件开发越来越需要协作的年代,写代码的时候,多站在让别人理解的角度考虑一下,会极大提升代码的可读性。在ThoughtWorks的招聘流程中,有一个Code Review的环节,拜这个环节所赐,我看过很多人的代码,不在少数的应聘者其代码唯一的优点就是完成了需求。以沟通为标准进行衡量,这显然是不够的。
同样,以沟通为标准,那么内部DSL显然在这方面做得更好,因为DSL本身就是为了让人更容易理解而存在的。这几篇文章中提供了很多内部DSL的手法,比如Method Chaining、Expression Builder等等。抛开DSL这样的BuzzWord,这些方法应该属于增强程序本身表达能力的方法。
JDK有一个很好的Method Chaining的例子:StringBuffer的append方法。
StringBuffer sb = new StringBuffer();
sb.append("log1").append("log2").append("log3");
这样的写法显然比下面的写法更为简洁,尤其是需要往StringBuffer中添加很多内容的时候。
StringBuffer sb = new StringBuffer();
sb.append("log1");
sb.append("log2");
sb.append("log3");
在我看来,这些内部DSL技术为我们打开了一扇窗,它让我们在编写代码,尤其作为API提供的代码时,有了一个新的思考方向。当然,并不是一味的应用这些内部DSL技术就会写出好代码,作为一个有经验的软件开发人员,我们需要一定的鉴别能力,分辨出究竟怎样做才会真正的提高代码的“沟通”能力。
之前写过两篇关于程序设计语言表达的blog(1、2),虽然这篇不像那两篇一样讨论语言的差异,但也算是在语言表达能力上的探讨吧! -
2008-03-11
Hello, Weka
Weka,是一个用Java编写的数据挖掘软件。数据挖掘,从字面上来看,它是一个从数据中找寻有用信息的过程,不过,它涉及的内容很多,所以,这里借用“分类”这一面来说事。
分类,从名称上来看,再简单不过了,给你一样东西,给它分个类。你如何知道怎么分类呢?显然,这是基于你已有的经验。对于计算机而言,这种经验从何而来呢?只有让人来告诉它,也就是说,我们要拿一批数据训练计算机,经过训练的计算机,便具备了一定的识别能力,就可以完成一些简单的分类工作。现实中,可以用到分类的机会有很多,比如我之前,曾经参与过的一个项目就是用这种方法来做车辆的识别。
下面便是一段使用Weka完成一段分类程序。
import weka.classifiers.Classifier;
import weka.classifiers.bayes.NaiveBayesMultinomial;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.StringToWordVector;
public class Main {
private static final String GOOD = "G";
private static final String BAD = "B";
private static final String CATEGORY = "category";
private static final String TEXT = "text";
private static final int INIT_CAPACITY = 100;
private static final String[][] TRAINING_DATA = {
{"Good", GOOD},
{"Wonderful", GOOD},
{"Cool", GOOD},
{"Bad", BAD},
{"Disaster", BAD},
{"Terrible", BAD}
};
private static final String TEST_DATA = "Good";
private static Filter filter = new StringToWordVector();
private static Classifier classifier = new NaiveBayesMultinomial();
public static void main(String[] args) throws Exception {
FastVector categories = new FastVector();
categories.addElement(GOOD);
categories.addElement(BAD);
FastVector attributes = new FastVector();
attributes.addElement(new Attribute(TEXT, (FastVector)null));
attributes.addElement(new Attribute(CATEGORY, categories));
Instances instances = new Instances("Weka", attributes, INIT_CAPACITY);
instances.setClassIndex(instances.numAttributes() - 1);
for (String[] pair : TRAINING_DATA) {
String text = pair[0];
String category = pair[1];
Instance instance = createInstanceByText(instances, text);
instance.setClassValue(category);
instances.add(instance);
}
filter.setInputFormat(instances);
Instances filteredInstances = Filter.useFilter(instances, filter);
classifier.buildClassifier(filteredInstances);
// Test
String testText = TEST_DATA;
Instance testInstance = createTestInstance(instances.stringFreeStructure(), testText);
double predicted = classifier.classifyInstance(testInstance);
String category = instances.classAttribute().value((int)predicted);
System.out.println(category);
}
private static Instance createInstanceByText(Instances data, String text) {
Attribute textAtt = data.attribute(TEXT);
int index = textAtt.addStringValue(text);
Instance instance = new Instance(2);
instance.setValue(textAtt, index);
instance.setDataset(data);
return instance;
}
private static Instance createTestInstance(Instances data, String text) throws Exception {
Instance testInstance = createInstanceByText(data, text);
filter.input(testInstance);
return filter.output();
}
}
这个程序分成两个大部分,前半部分用以训练分类器,后半部分则是测试这个分类器。
训练分类器,我们要做的包括,选择分类算法和准备训练数据。在Weka中,每一种分类算法都是Classifier的一个子类,这样的话,就可以在不改变其它部分的情况下,很容易的修改分类算法。
其实,稍微了解一下这方面的知识的人,都会知道,分类算法固然重要,但真正决定一个分类器本事大小的,是用以训练的数据。想要得到一个好的分类器,少不了不断调整训练数据和不断的训练。这同人类认识问题是一样的,经得多,见得广,才有更好的分辨能力。在Weka中,用以训练的数据就是Instances,顾名思义,这是Instance的复数,显而易见,单独的一个训练数据就是Instance,而Instances这个类的存在,可以把Instance的一些公共的属性放到一起。在这里,我们可以看到,为了用文本作为训练数据,我们会把文本转换为Instance。同样,测试分类器的时候,我们也会把文本转换为一个Instance,然后再进行分类。
除此之外,这里还有一个Filter的概念,同常见的filter概念类似,它给了我们一个进行正式处理之前,对数据进行处理的机会。在这里,主要是对Instance做一些相关的变换。
当我们得到一个分类器之后,就可以利用这个分类器进行分类了,其中,最关键的代码是
classifier.classifyInstance(testInstance);
这段代码返回的是根据分类算法计算结果得到的一个相似度,我们可以利用这个值来估计我们测试用的数据应该属于哪个分类。
从代码上来说,这段代码本身并不复杂。正如前面所说,一个好的分类器是需要让数据帮忙的。所以,换几个测试数据,你就会发现,这段代码中实现的分类器一点都不强大。如果希望它强大起来,扩展训练数据是一个必然的结果。不过,对于这篇blog而言,这不重要,因为我们只是要和Weka问个好,进一步的工作,还需要进一步的努力。 -
2008-03-06
Fluorida 0.0.1发布了!
Fluorida是一个Flash的功能测试工具。如果你听说过Selenium,那么可以把Fluorida理解为它对应的Flash版本。
前不久,gigix对我说,他打算做一个Flash的功能测试工具。我说,从语言的角度来说,我不喜欢Action Script,因为它缺乏美感,但我喜欢这个方向,所以,我觉得这件事靠谱。
上周末的Open Party,听了Michael Chen一个关于Rich Client的session,顺便清理了一下关于Rich Client发展的思路。C/S年代,最大的问题在于部署,升级起来很困难,进入到B/S年代,浏览器的广泛存在解决了部署的问题,不过,简单的页面表现力受到了极大的限制,所以,才有Ajax这样技术的流行。把部署和UI表现力一下子都解决了,那么服务器和客户端的威力就可以得到极大的提升。显然,一些公司看到了这方向,比如MS,它们祭出了Silverlight,不过,从目前的状况来看,在这个领域的领跑者无疑是Adobe的Flash。因为几乎所有拥有浏览器的计算机都安装了Flash Player,这是一个压倒性的优势。
最初的Flash,是为设计者而存在的,所以,谈到Flash,人们首先想到的是“炫”,显然,这不是程序员的强项,所以,大多数开发人员并不会和Flash太亲近。Adobe认为Flash应该扮演更重要的角色,比如成为前面提到的新一代C/S结构的领军人物,但是,想做到这一点,必然需要大量开发人员的支持,所以,Adobe不断的让Flash进化着,比如,Action Script从2到3,发生了巨大的变化,用Action Script 3加入了面向对象,用它写程序,感觉和用通常的程序设计语言并无二致。Adobe甚至更近一步推出了Flex,实际上,它就是为开发人员提供的Flash。再在Eclipse的基础上,打造出Flex Builder,所有这一切都是为了亲近开发人员。Adobe AIR的推出,让Adobe在这方面野心显现无疑。可以看到的是,Adobe的脚步并未停止,它还打算让更多的语言运行在Flash上,显然,它们要提供的是一个新的平台,用以抗衡.NET和Java。
站在开发者的角度,我们更关心怎么让自己的开发工作更舒服一点。作为一个ThoughtWorker,没有测试的日子是让人难以忍受的。在之前的一个进行Flash开发项目中,FlexUnit成功填补了Flash开发拼图的单元测试框架这块,而功能测试这块却一直没有很好的做起来,有人尝试过FunFX,但总觉得不爽。
当gigix要写一个Flash集成测试工具的时候,我知道,参与过Selenium开发的他,对于功能测试应该是什么样子,心里应该很有数。事实就是这样,从接口上来看,Fluorida与Selenium如出一辙。
欢迎任何对这个项目感兴趣的人加入,0.0.1意味这个项目中有许多事可做,你可以给出你的建议、意见或是代码,甚至你觉得这个项目的名字不好也可以建议修改。因为这个项目最开始叫做Fluorine,由于与一个Remoting框架相同,gigix把它改成了现在的Fluorida。 -
2008-03-03
出门在外
俗话教导我们,在家千日好,出门一日难。
最近,因为工作需要,我有一段时间没有在自己办公室,而是在客户现场。按说,在那里,人家对我们客客气气的,尽可能帮助我们解决遇到的问题,而且,一般这种情况下,都是吃得好喝得好的,应该没有什么不舒服的。但是,无论如何,这些日子总是让人感觉很不爽。今天临下班的时候,当大家决定明天都回公司的时候,项目组的同事们几乎欢呼起来。
无论是在西安,还是在北京,甚至是在班加罗尔,只要是在ThoughtWorks的办公室,我都是感觉很放松,因为我是这里的人,在这里,我可以肆无忌惮。到了客户现场,下意识就会绷紧一根弦,无论说什么做什么都要小心翼翼,毕竟在外面,无论你愿意不愿意,你都代表着公司形象,为了不给公司丢脸,做什么都要经过一下大脑。所以,我觉得在客户现场,即便一天什么都不做,都会觉得很累。
其实,我早就知道,在别人的地盘不好玩。离开东软之前的最后一年,我就是被发配到北京的一家公司,进行现场的开发。按理说,那的物质条件比东软要好很多,而且,经常有机会参加那里组织的活动。但那一年,是我最为压抑的一年。在那里,虽然从来没有人故意另眼相待,但我总觉得自己不属于那里,虽然在那里工作将近一年的时间,却从未真正融入那里的圈子。或许,这种心灵上没有归属感坚定了我离开东软的信念吧!
我还听说过另一个关于在外工作的故事。我之前工作的一个部门,派了很多人到美国去做开发。在很多人看来,这些人有机会出国,而且拿着相对较高的补助。而事实是,那些去之前对美国无限向往的家伙,从美国回来之后,都对彻底那里失去了兴趣,甚至闻之色变。当然,这其中有很多故事。
总而言之,在外不如在家好。无论如何,明天要回办公室了!







