作者:张龙

出处:http://www.infoq.com/cn/news/2013/07/zhengye-on-moco

郑晔谈Moco框架的开发:写一个好的内部DSL,写一个表达性好的程序

作者 张龙 发布于
七月 19, 2013 | 讨论

Moco是一个简单搭建模拟服务器的程序库/工具,这个基于Java开发的开源项目已经在Github上获得了不少的关注。该项目的简介是这样描述自己的:

Moco是一个简单搭建stub的框架,主要用于测试和集成。这个框架的开发灵感来自Mock框架,如MockitoPlayframework

为什么要开发这个框架?

集成,尤其是基于HTTP协议的集成——web service,REST等,在我们的项目开发中被广泛应用。

以前,我们每次都要往Jetty或Tomcat等应用服务器上部署一个新的WAR。大家都知道,开发部署一个WAR的过程是很枯燥的,即使在嵌入式服务器上也是如此。而且,每次我们做一点改动,整个WAR都要重新组装。

Moco的出现,正是为了解决这些问题。开发团队只要根据自己的需要进行相应的配置,就会很方便得到一个模拟服务器。而且,由于Moco本身的灵活性,其用途已经不再局限于最初的集成测试,比如,Moco可以用于移动开发,模拟尚未开发的服务;Moco还可以用于前端开发,模拟一个完整的Web服务器,等等。

之前的《企业系统集成点测试策略》一文曾对Moco的基本用法和背后的一些理念进行了介绍。今天,我们邀请到了Moco框架的开发者郑晔,来跟我们分享一下Moco设计背后的一些思路。

InfoQ:郑晔你好,能否向大家做个简单的自我介绍、现在所从事的工作以及感兴趣的方向。

郑晔:大家好,我是郑晔,一个有十多年工作经验的程序员,现在在ThoughtWorks工作。这些年做过很多事情,包括开发和咨询,除此之外,做过演讲,也写过文章,翻译过书,也贡献过开源,愿意与人畅聊技术,也愿意分享自己的经验。个人一直热衷于探索各种程序设计语言在真实软件开发中所能发挥的威力,致力于探寻合理的软件开发方式。我的blog是梦想风暴,新浪微博是@dreamhead

除了日常开发之外,近期的一项工作是,整理现代Java开发的一些知识,因为Java从诞生至今已经快20年,但现在很多Java程序员的代码编写方式和开发理念还停留在至少10年前。这些年,我涉猎过很多不同的程序设计语言和不同的项目,把其中学到的一些理念应用回Java开发,可以很大程度上提升Java原本笨拙的方面。我准备整理一下自己在这方面的理解分享给其他人。一些内容已经写成《你应该更新的Java知识》系列,发布在自己blog上。其中涉及到很多函数式编程的思想,比如,使用函数组合进行构建,这样的思想已经很好地体现在Moco的内部DSL设计上。

InfoQ:对于企业系统的集成测试,如果采取自行开发模拟服务器的方式,想必需要投入不少人力与时间成本,Moco在这方面的考量是什么,它是如何解决这个问题的?

郑晔:在企业级开发里,集成几乎是躲不开的。为了不耽误开发进度,开发团队通常都会开发一个模拟服务器,模拟要集成的服务。从十多年前我正式工作的第一个项目开始,我就在做这件事。当时刚开始工作,非常生猛,我直接从底层的Socket开始直接编写了一个Web服务器,支持我们所需的服务。后来,在各种不同的项目中,我经常遇到类似的情形,不断重复地构建着自己的模拟服务。构建服务的过程,非常繁琐,我也尝试过很多不同的框架做这件事,比如,Servlet、Ruby的Sinatra等。即便有框架支持,后期调整服务的过程也还是非常麻烦,比如Servlet需要重新部署,这是让人无法忍受的。

Moco正是为了解决这种问题而生的。目前,Moco直接支持的使用方式有两种,一种是Java API,另一种是独立服务器。Java API可以在我们在单元测试框架里,通过简单的配置,运行起一个模拟服务,而独立服务器的方式,则可以通过一个配置文件,配置模拟服务器。无论是Java API,还是独立服务器,相对于传统做法,Moco有两个非常明显的优势,一是提供DSL,可以非常直白地表现目的,二是启动速度非常快,无需漫长的等待。

InfoQ:相比于传统的Jetty容器结合Mock框架(如EasyMock等)方式来说,使用Moco有哪些优势?

郑晔:我不知道是否真的有人这么用过。Jetty一般是用在集成测试里,而Mock框架则是用在单元测试里,两种测试有着根本的不同。

我知道有类似于Moco的框架采用了Jetty作为底层的支持,但Moco没有走这条路。Jetty的目标是做一个Web Server,而不是一个模拟服务器,所以,以模拟服务器的需求看,它会有些重。另外一点,未来Moco可能不只做Http服务,也可能会做Socket服务。现在已经有些人向我提出了这方面的需求,也许会在后面的版本里实现。

至于Mock框架,其主要是在对象级别进行模拟,而Moco是在模拟服务,二者模拟目标的级别是不同的。

InfoQ:Moco的设计采用了内部DSL方式,请问其实现原理与好处有哪些?

郑晔:我一直对程序语言的表达性有着非常强的追求,表达性好的程序,理解起来也更容易,无论是现在的开发之时,还是后续的维护之日,都有极大的助益。我个人即便是选择使用程序库,也会倾向于选择表达性好的程序库。而DSL就是表达性在程序设计语言中最好的体现。我是Martin Fowler《领域特定语言(Domain Specific Language)》这本书中文版的译者之一。翻译DSL这本书的过程,加深了我对DSL的理解。

Moco的基本想法其实早就在我脑子里了,但我用了很长时间去找的一种把它写成内部DSL的方式。我从Mockito中受到启发,构想出描述服务端的方式,就是现在的请求/应答的基本结构,但怎么把它更好地运行起来一直困扰着我,直到有一天,我看到PlayFramework在测试方面的做法,豁然开朗,于是有了现在的running结构。

虽然有了思路,但写出一个好的内部DSL也不是一蹴而就的。一方面,设计一个可读的API并不是容易,选择哪些名词、动词、连接词着实费了我很多思量,往往一个API的设计要花很长时间来起名。另一方面,Java不支持集合字面量,所以,很多东西为了表达性只好采用函数,用其返回的对象表示一个基本概念,但Java的类型系统又有很多限制,比如,同样在请求和应答里都可以有文件的概念,但如果想用同一个语法表达文件这个概念,就必须有个中间层,分别适配请求和应答。

InfoQ:Moco还可以独立服务器的方式使用,这种方式与传统的Tomcat方式相比的优势有哪些、劣势有哪些、功能上有哪些差异性?

郑晔:无论是Tomcat,还是Jetty,它们首先是一个Servlet容器,是为了部署Servlet存在的,本身要支持Servlet的诸多特性,所以,对于模拟集成服务器这个需求而言,都是非常复杂的,Moco只是针对模拟服务这个特定的需求,就要简单很多。这样的差异有一个非常直观的体现:启动速度。我演示Moco时,很多人都惊讶于Moco飞快的启动速度。

这里面有一个很重要原因是,Moco最先实现的是Java API,初衷是为了把它用在单元测试里。如果启动速度慢,就没人会用了。所以,在开发时,我没有选择一个已有的容器,而是选择了一个Netty这个网络应用开发框架作为基础。

当然,Moco毕竟只是一个模拟服务器,它不像Servlet容器功能那么强大,可以做许多事情。说来有趣,在开发Moco的过程中,我曾冒出过这样的念头,让Moco支持函数,这样,用户就可以自定义一些简单的行为了。但后来,我打消了这个念头,一旦引入函数的概念,Moco就开始像一个真正的服务器,而不再是一个纯粹的模拟服务器,和我的初衷不一致了。也许有一天,我会写一个基于Moco理念的新Web框架,但它绝不是Moco。

其实,Moco独立服务器是个无心之作。我最初只是想实现Java API。后来看到一些类似的框架支持配置文件的形式,就顺手写了一个。没有想到,它会成为很多人使用Moco的一种重要方式,它也反过来影响到Moco核心API的设计,比如,现在Moco涉及的文件部分都是在运行时动态加载的,这就是为了满足独立服务器运行的需要,不必为了修改一个文件,反复重新启动。

InfoQ:在开发Moco时曾遇到过哪些棘手的问题?

郑晔:从技术上说,Moco不是一个很复杂的东西,实现难度不是很大。Moco最初的一个发布版,只有不到3000行代码。但这里面也有一些我的思考,我倾向于把事情做简单,因为把代码写多很容易,而写少却不容易。简单的东西维护和扩展都相对容易一些,才会走得更长远。无数的经验已经证明,复杂的东西只会为自身所累。我做咨询的时候,看到过很多动辄百万代码的项目,项目里的所有人都很挣扎,我们作为咨询师经常可以很轻松地把代码规模砍到原来的一半,因为里面重复代码太多。最近,我的一个同事为一个电信项目设计了一个新的模型,原来数百万行的代码一下子缩减到十万的规模,这是数量级的提升。很多软件最初的设计都是不错的,只是往往在最不起眼的写代码层面上搞砸了一切。我曾经在InfoQ上写过一个系列的《代码之丑》,就是我在各种项目中看到的代码没写好的地方。

Moco开发中如果说有一段棘手的日子,那是大约在开发两个月之后,所有的基础功能都实现了,我一度认为Moco的开发到此结束了。但后来,我把Moco介绍给更多的人,有人给我更多反馈,有人开始把Moco应用到实际的项目中。新的需求也就随之而来了,才有了后来熊节写的那篇文章《企业系统集成点测试策略》。而且,随着应用越来越广泛,各种新的用法也就涌现出来了,有人在移动开发中使用Moco模拟服务端,可以在没有真正服务端的情况下,进行移动端的开发;有人在前端开发中使用Moco模拟服务端,以便快速建立原型。所以,作为一个开源项目,真正生存下来的动力是有用人用,而不只是闭门造车。

编辑注:在今年的Duke's Choice Awards上,Moco框架被提名为最具创新力的Java项目之一。文中提到的《企业系统集成点测试策略》一文同时也在InfoQ英文站发布过,并在Twitter上得到了Martin
Fowler的关注。

郑晔谈 Moco 框架的开发:写一个好的内部 DSL ,写一个表达性好的程序的更多相关文章

  1. 郑晔谈 Java 开发:新工具、新框架、新思维【转载】【整理】

    原文地址 导语:"我很惊讶地发现,现在许多程序员讨论的内容几乎和我十多年前刚开始做 Java 时几乎完全一样.要知道,我们生存的这个行业号称是变化飞快的.其实,这十几年时间,在开发领域已经有 ...

  2. 12小时包你学会基于ReactMix框架的ReactNativeApp开发(二)基于Css+HTML写第一个app页面

    上一篇文章,大家对于ReactMix(https://github.com/xueduany/react-mix)框架有了一个基本认识,知道我们是一个语法糖,帮助大家基于一套代码,所有平台都能跑.那么 ...

  3. 浅谈angular框架

    最近新接触了一个js框架angular,这个框架有着诸多特性,最为核心的是:MVVM.模块化.自动化双向数据绑定.语义化标签.依赖注入,以上这些全部都是属于angular特性,虽然说它的功能十分的强大 ...

  4. 轻轻谈一下seaJs——模块化开发的利器

    "仅做一件事,做好一件事." 这个应该就是seaJs的精髓了. 我在自己的一些项目中使用过seaJs.对其算是了解一二.如今就班门弄斧.轻轻地谈一下. 首先上一段度娘的话: &qu ...

  5. 使用 CodeIgniter 框架快速开发 PHP 应用(六)

    原文:使用 CodeIgniter 框架快速开发 PHP 应用(六) 简化使用 Session 和安全理论说得够多了! 现在让我们开始写我们自己的应用. 在这一章里,我们将会大致描述一下我们要建立的一 ...

  6. Mock1 moco框架的基本介绍

    前言: Mock就是模拟接口的,一般在开发人员还没有开发完接口,但是有接口文档,这个时候就可以执行接口测试,前端同学也可以用mock功能给自己使用. 功能:可以模拟http协议发送请求 下载链接:ht ...

  7. 手撸ORM浅谈ORM框架之基础篇

    好奇害死猫 一直觉得ORM框架好用.功能强大集众多优点于一身,当然ORM并非完美无缺,任何事物优缺点并存!我曾一度认为以为使用了ORM框架根本不需要关注Sql语句如何执行的,更不用关心优化的问题!!! ...

  8. 『居善地』接口测试 — 12、Moco框架介绍

    目录 1.Mock功能介绍 2.Moco框架介绍 3.Moco框架在接口测试中的作用 4.Moco框架的优点 5.Moco框架的下载与启动 (1)Moco框架的下载 (2)Moco框架的启动 1.Mo ...

  9. 小谈Jquery框架

    现在Jquery框架对于开发人员基本上是无人不知,无人不晓了,用起来十分的方便,特别是选择器十分强大,提高了我们的开发速度.但是好多人也只是停留在了会用的基础上,我个人觉得会用一个框架不算什么,只能说 ...

随机推荐

  1. 网络教程(12) TCP协议

    IP协议的限制 IP协议需要 datalink帧来包装它 Ethernet或者PPP 一般都有1500byte字节或者大小的限制 可能会出现的问题 Packet loss – retransmit R ...

  2. PHP中调用Soap/WebService

    关于在PHP中如何调用Soap/WebService的描述,网络上有不少帖子.但是主要讲述了如何用PHP开发服务器端.客户端并加以关联,而很少触及在PHP中调用现成的WebService的情况.在本文 ...

  3. Gym-101615D Rainbow Roads 树的DFS序 差分数组

    题目链接:https://cn.vjudge.net/problem/Gym-101615D 题意 给一棵树,每个边权表示一种颜色. 现定义一条彩虹路是每个颜色不相邻的路. 一个好点是所有从该节点开始 ...

  4. [luogu1600 noip2016] 天天爱跑步 (树上差分)

    题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵 ...

  5. fun(int **p)的使用

    #include <iostream>using namespace std;void fun(int **p){ cout << p[0][0] << endl; ...

  6. python第四周:装饰器、迭代器、内置方法、数据序列化

    1.装饰器 定义:本质是一个函数,(装饰其他函数)就是为其他函数添加附加功能 原则:不能修改被装饰函数的源代码,不能修改被装饰函数的调用方式 实现装饰器的知识储备: 函数即“变量”.每当定义一个函数时 ...

  7. TensorFlow 制作自己的TFRecord数据集

    官网的mnist和cifar10数据之后,笔者尝试着制作自己的数据集,并保存,读入,显示. TensorFlow可以支持cifar10的数据格式, 也提供了标准的TFRecord 格式,而关于 ten ...

  8. ibatis的批处理

    (1)spring模式:尽管spring已经配置了事务,但以下代码中还是要设置事务,不然batch不会起作用;另外这里虽然设了一下事务处理,但对全局事务并不会造成影响;注:不启用事务将建立多次连接,这 ...

  9. gitlab一键安装 笔记

    0 简单介绍bitnami和gitlab bitnami BitNami是一个开源项目,该项目产生的开源软件包安装 Web应用程序和解决方式堆栈.以及虚拟设备. bitnami主办Bitrock公司成 ...

  10. Android适屏

    总结一下自己的适屏经验,仅仅希望自己不断进步,不断完好,假设有热心肠的"前辈"指导一下,不胜感激! Android5.0已经出来了,说是这个版本号对Android屏幕适配做了非常多 ...