• 因为Moco,我得到了一个演讲的机会,在QCon上海的上做一个关于Moco的分享。上一次参加QCon还是几年前的事,短短几年时间,QCon已经成为了中国技术人员心目中干货众多的顶尖技术大会。参加这样的大会,分享只是其中很小的一部分,更多的是,出来见见世面,看看别人在做些什么,结交些新朋友。

    如今,国内技术社区的分享精神要比以往好了很多,这次大会大公司都站了出来,BAT的分享有很多,新浪、搜狐、豆瓣等也有自己的分享。这和前些年都是靠个体热情做社区、做大会的状况已经有了很大的转变。我依然记得很早以前,一说大会,基本上就会和广告挂钩的印象。而且,同阿里人聊天时,听说他们现在要在公司内成为高级技术专家,很重要的一个指标是,在社区内的影响力。能够把这个指标纳入考量体系内,确实是一个很好的引导方式,这也不难理解,为什么阿里系最近几年眼瞅着在技术社区内的名声越来越好。其它大公司在这个方面也有长进,但还有很长的路要走。

    出来讲是进步,但讲东西的水平实在让人不敢恭维。讲东西,最重要的是面向观众。遗憾的是,很多讲师就是在为自己讲,根本没有考虑过用怎样的一个思路讲能够更有代入感,更能让人接受。有一个腾讯的演讲,里面常常听到讲师说,这也没什么,那也很简单,一笔带过很多东西。实在是愧对那么多冲着大公司名头去的观众,也实在砸自己公司的牌子。当然,在后续的评分环节里,我很客气地给他打了很低的分数。

    其实,作为讲师,我是知道的,在这次大会之前,InfoQ专门请了专业的演讲培训师给讲师们做培训,我因为人不在京沪两地而遗憾错过。这些大公司的很多演讲者很多人都离京沪不远,如果没参加这个培训算是遗憾,如果参加了,那还真对不起老师。

    我个人最喜欢的一个分享是来自Facebook的,他介绍了Facebook的一个开源项目React.js,对于这个东西,我是完全意义上的门外汉。但他一步一步地把React.js的来龙去脉讲得非常清楚,在我脑子里留下了深刻的印象。在未来的技术选型里,如果有相关的场景,我会考虑使用这个项目的。

    其它在脑子中留下印象的东西还有,

    • github提出的“干掉”Product Owner,实际上是说全功能团队
    • github的Hubot,构建团队自己的“机器人”,把一切尽可能自动化
    • 豆瓣的CODE,用一个无人管理的项目,打造构建一种工程师文化
    • 百度的Clouda,快速构建跨终端应用
    • 阿里的朴灵分享关于Node.js的设计与实现,理清了Node中最基本的几个概念:事件、非阻塞、Watcher等
    • 腾讯的QZone跨终端应用,用Node.js做代理,把JS模板渲染搬回了服务器

    在QCon期间,还找来了几个自媒体人来“扯淡”:鬼脚七、池建强、蔡学镛、道哥、厉哥,从IT老男人开始讨论,大家弄得很欢乐。遗憾的是,这几位少有还拼搏在第一线的程序员,多多少少给人一些误导,似乎程序员发展方向都不再是程序员了一般。也许矛盾就在于此,能一门心思写程序的,估计没他们几个这么能扯。:)

    最后,感谢一下InfoQ,给了我一个机会,在门票高达几千块钱的情况下,不仅让我没花钱买票,还包吃包住包往返路费的,顺便夹带私货介绍自己的开源项目。如果你也想免费蹭大会,不妨去做个讲师。

  • 2013-11-02

    Moco 0.9发布

    Tag:moco

    前版信息:Moco 0.8.1发布

    我很高兴地宣布,Moco 0.9发布了。

    Moco是什么?

    Moco是一个可以轻松搭建测试服务器的框架/工具/程序库。

    变更

    本次发布包含两个重大的新特性,事件和验证。

    事件指的是,在某种情况下,触发相应的处理。目前支持的事件是“完成”事件,也就是说,当一个请求处理完成时,我们可以做相应的处理。

    比如,我们可以用如下代码在请求处理之后,发出另外一个请求。

    server.request(by(uri("/event"))).response("event").on(complete(get("http://another_site")));

    我们还可以声明这个请求是异步的,这样不会阻塞本次调用给客户端回应答。

    server.request(by(uri("/event"))).response("event").on(complete(async(post("http://another_site", "content"), latency(1000))));

    这种情况可以用于诸如OAuth认证之类的情形,在服务器接到一个请求之后,发起另外一个请求。

    它也支持相应的JSON配置:

    {
    "request": {
       "uri" : "/event"
    },

    "response": {
       "text": "event"
    },
    "on": {
       "complete": {
           "async" : "true",
           "latency" : 1000,
           "post" : {
               "url" : "http://another_site",
               "content": "content"
           }
       }
    }
    }

    验证,是一个用于测试代码中的特性,类似于Mock框架中的verify功能,对请求进行验证。下面是一个例子。

    RequestHit hit = requestHit();
    final HttpServer server = httpserver(12306, hit);
    server.get(by(uri("/foo"))).response("bar");

    running(server, new Runnable() {
     @Override
     public void run() throws Exception {
       assertThat(helper.get(remoteUrl("/foo")), is("bar"));
     }
    });

    hit.verify(by(uri("/foo")), times(1));

    这里的verify的第一个参数是一个matcher,也就是说,我们可以写任意的条件组合,甚至与设置服务器的完全不同。

    此外,我们还支持未预期请求的验证,如下:

    hit.verify(unexpected(), never());

    目前支持的验证条件包括:

    • never: 没有收到这样的请求。
    • time: 准确匹配收到这些次数的请求。
    • atLeast:至少收到这些次数的请求。
    • atMost:至多收到这些次数的请求。

    此外,还有一些小特性的变更:

    • 缺省情况下,关闭了使用API方式的日志。
    • seq API支持了ResponseHandler接口。
    • 为PUT和DELETE方法增加了快捷方式。

    在内部实现细节上也有一些微调:

    • 如果不提供端口,服务器会自行选择可用端口。
    • 在Failover的实现里,“statusCode”改成了“status”。如果因此造成了测试失败,可以手工修改Failover文件。

    感谢

    感谢Paul Hammant,为Moco提供了大量的反馈,并激发了验证特性实现的灵感。

  • 2013-10-18

    Moco,一岁了!

    Tag:Moco

    一年前的今天,我在github开启了一个新的小项目,为的是解决自己在日常开发中遇到的集成问题,它叫Moco

    一年后的今天,Moco一岁了!

    下面是一些Moco在这一年里的成果:

    • 发布了3个版本,从2013年5月1日发布0.7开始。
    • 有了524次提交,10个贡献者,42个Issue。
    • 在github上有了199个star。
    • 从2013年2月17日开始,迄今已经有连续243天的提交。
    • 分别有人写了Maven和Gradle插件
    • 有人给Moco写了Scala绑定

    更荣幸的是,在这一年里,Moco

    • 夺得了ThoughtWorks中国区第二届技术大赛最具技术创新奖。
    • 荣获2013年Duke选择奖。

    这一年里,Moco得到了更多的关注:

    • 越来越多的人把Moco用在了自己的项目上,于是,我得到了更多的反馈。
    • 越来越多的人开始关注Moco,许多网站报道了Moco,它还登上《Java Magazine》。

    启动Moco之时,我没想过这个项目可以走多长时间。一年后,Moco路越走越宽。

    Moco,继续前进!

  • 你应该更新的Java知识之常用程序库(一)
    你应该更新的Java知识之常用程序库(二)
    你应该更新的Java知识之构建工具
    你应该更新的Java知识之Observer
    你应该更新的Java知识之集合初始化
    你应该更新的Java知识之集合操作
    你应该更新的Java知识之惰性求值
    你应该更新的Java知识之Optional
    你应该更新的Java知识之Optional高级用法

    在传统的Java里,为了表示一个集合,我们常常会写出这样的代码:

    public class People {
        private List people;

        public void setPeople(List people) {
            this.people = people;
        }

        public List getPeople() {
           return this.people;
       }

         ...
    }

     严格说来,这样的代码存在缺陷。虽然貌似List被封装到People里,但实际上,这个List的引用却暴露在外面,这个类的用户可以轻松地拿到List的引用,随意修改。所以,下面是一种更严谨的写法:

    public class People {
        private List people;

        public void setPeople(List people) {
             this.people.addAll(people);
        }

        @SuppressWarnings("unchecked")
        public List getPeople() {
            return (List)this.people.clone();
        } 

        ...
    }

     这里的做法基本思路是,做一个副本,保证内部引用(这里的people)不会传播到外面。但在实际编写代码的时候,大多数人都不会这么做,能否意识到这样的问题只是一个方面,这样的代码写起来,也要比原来的那种写法麻烦得多。按照许多公司的做法,这种要求只能放到代码规范里,但无论如何,在程序世界里,人为规定永远是最容易忽略的约定。

     不过,在真实世界中,即便是我们写的只是上面的最简单那种形式,却很少出现问题。原因何在呢?因为大多数情况下,我们编写这样程序的时候,会有一种隐形的约定,这个“List”是不变的。我们设置(set)完这个List之后,基本上不会留着它的引用在做任何操作;而得到(get)它之后,也基本上不会去修改它。

     在这种情况下,我们使用的实际上是一个不变的List。既然是一个不变的List,那不如就更直接地把它表现出来。

     Guava为我们提供了不变集合的概念,对应着各种具体类型,有ImmutableList、ImmutableSet,还有ImmutableMap。从名字上,我们不难看出它们的用法。

     Guava不变集合的做法并不是另起炉灶,而是遵循了Java已有集合框架的约定。比如,通过查阅文档,我们不难发现,ImmutableList就是一个List,只不过使用这个“List”,只不过,当我们尝试调用诸如add、set之类试图修改集合的方法时,它会抛出异常。正是有了这样的基础,不变集合可以和很多已有的类库、框架配合在一起。

     有了这个基础,我们把不变集合的概念应用于之前的代码,它就是下面这个样子:

    public class People {
        private ImmutableList people;

        public void setPeople(ImmutableList people) {
            this.people = people;
        }

        public ImmutableList getPeople() {
            return this.people;
        } 

        ...
    }

    这样一来,代码依然很简单,但是,意义却与从前完全不一样了。我们不必再为可能存在的隐忧顾虑了:一旦元素放到集合里,就不可能修改它了,因为它是不可变的。

    对于使用这段代码的人来说,getter自不必说,如往常一样使用,setter也不费力。只是当做字面量使用的集合,我们已经在《你应该更新的Java知识之集合初始化》中讨论过了。如果要适配于现有程序库,把一个已有的List转成ImmutableList也不复杂,一个简单的copyOf方法就可以实现:

        List existingPeople = ...
        ImmutableList immutablePeople = copyOf(existingPeople);

    为了让讨论更完整,这里不得不提到另外一个不变接口的选择:Iterable。它是从Java 5开始进入JDK的一个接口:

    public interface Iterable  {
        Iterator iterator();
    }

    作为一个Java程序员,我们对Iterator简直再熟悉不过了,有了Iterator,我们就可以遍历一个集合。所以,在一些情况下,我们也可以使用Iterable表示一个不变集合。大多数Java已有的集合类都实现了这个接口。

    既然它是JDK里的东西,为什么不把它优先推荐呢?原因有几个。最重要的一个原因在于我们熟知的Iterator,它有一个方法叫做remove,可能大多数Java程序员已经习惯性地忽略了这个方法,但它就在哪里,它是一个可以“改变”集合的方法,所以,从语义上说,它不是一个很好的选择。另外,从已有的代码习惯来说,很多程序员还是很喜欢用List、Set作为接口,所以,ImmutableList从心理上来说,更接近已有的习惯。剩下的一个点似乎不那么重要,有些代码真的需要使用到特定类型的接口。不过,就大多数代码而言,我们只是要得到的一个集合,做一些操作,而这些操作我们在《你应该更新的Java知识之集合操作》中做了一些讨论。

    在函数式编程中,不变是提升程序稳定性一个很重要的概念。既然我们大多数情况下实际用到的是不变集合,那就不妨直接把它表现出来。

  • 2013-09-22

    打开视野

    在ThoughtWorks里,我面试过许多应聘者,许多人在原来公司都是公司里的干将、主力。按说,也都算是很优秀的程序员了,不过,在面试过程中,折戟沉沙的也不在少数。

    这样的面试者往往是工作时间不长,大约是三五年,在小问题的解决上具备了一定能力,但没有什么机会面对大问题,所以,基本上都是在处理别人嚼碎的具体问题。所以,这样的求职者在面试过程中所展现的能力,以ThoughtWorker的标准看,编程技巧上没有多么突出,陈述问题显得零散而没有重点,至于宏观问题的思考,几乎是没有的。

    其实,这不是一个偶然现象,我已经见到不知多少类似的求职者。通过和他们的交流,我会发现,实际上,这些人往往不是不想提高,甚至他们都是非常努力地在工作。只是在我看来,他们所处的环境限制了他们的视野,一叶障目,不见泰山。在工作中,项目负责人给他们的工作就是解决一个具体问题,至于真正有思考的问题,那些负责人自己已经解决了。

    这样的求职者以目前展现的能力,可能不足以让他们成为一个ThoughtWorker,但还是有不少求职者在这个过程中认识到自己的差距,主动寻求一些建议。而本着好为人师的态度,遇到这样有心成长的求职者,我也会给他们一些建议。

    对于一个人的成长来说,视野很重要。如果只看到周边的这些人,往往会有一种错觉,我已经做得很好了,而这种“很好”只不过是一个局部峰值,有时,这个局部峰值只是一个很低的标准。我们只有把自己放在一个更大的平台上,才会对自己的定位有个更清晰的评判。

    如今已经不是那个“铁饭碗”抱到老的年代了,我们也不指望为一直在一个公司工作。所以,我们也不应该把一家公司的衡量标准作为自己的奋斗目标。我们是用能力在这个业界生存,我们的目标至少也应该是以业界的标准来看。

    很多人往往倾向于从周边的人学习,但当周边的人不能给我们更好的帮助时,我就需要寻找更好的成长途径。很幸运,程序员这个群体是最容易拓展视野的群体。我们几乎每天都在和互联网打交道,在上面我们可以见识到更大的天地,见识到更多高水平的人。我们可以明里暗里地向这些人学习,学习的不仅仅是他们的能力,更重要的是,他们的思考方式。

    读书,对于程序员来说,也是一种开拓视野的重要方式。基本上,能记录到书里的内容都是作者的深入思考,而且,相比于网上零散的内容,书上的知识会更系统。这是很好地向业界领先或成熟的思想直接学习的机会。当然,这里说的是那些值得看的经典图书。至少从我自己的了解上看,网上给程序员推荐书的帖子已不胜枚举。

    对于程序员来说,另外一个开阔视野的方式是,走出去,参加一些程序员的聚会。无论是北京、上海,还是西安、成都,这几个我常驻过的城市都有很好的程序员社区。每年还有各种各样的技术大会在不同的地方召开。在那里,我们会看到许多不同的程序员,知道他们在做着各种各样有意思的事情,还会接触到许多经验丰富的程序员,他们一定对个人成长有着自己的思考。参加这种聚会,与人的沟通,我们不需要付出任何代价,成本几乎为0。

    至于学什么,怎么学,那都是视野打开之后的事情了,固步自封是最可怕的事情。

    最后是我自己的成长小故事。记得当年我在东软的时候,我给自己的一个评价是自己写程序的能力处于“过饱和”状态,因为我的工作不需要我把程序写得那么好。如果我按照周边人的标准评判自己,我已经发展到非常不错的水平,似乎已经有了足够的理由固步自封。很幸运,我没有。在沈阳,没有很好的程序员圈子,我除了和身边几个高手接触外,更多的是在网上如饥似渴地看着各种各样的东西。

    当东软无法满足我的求知欲时,我就开始寻找下一个能帮助自己进一步提升的地方,很幸运的是,我找到了ThoughtWorks。在这里,我每年都能不断地看到新东西,学习新东西。所以,我从未停下过。