写点什么

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

  • 2013-07-19
  • 本文字数:3852 字

    阅读完需:约 13 分钟

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

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

为什么要开发这个框架?

集成,尤其是基于 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 的关注。

2013-07-19 02:317092
用户头像

发布了 88 篇内容, 共 264.2 次阅读, 收获喜欢 8 次。

关注

评论

发布
暂无评论
发现更多内容

极客时间运维进阶训练营第十三周作业

9527

Java高手速成 | 单例模式实现方式——枚举

TiAmo

单例模式 枚举 Java 开发

对线面试官:浅聊一下 Java 虚拟机栈?

王磊

java面试

算力新话题,畅聊算力之新民生

鲸品堂

算力网络 企业号 2 月 PK 榜

第六周作业-拆分电商系统为微服务

不爱学习的程序猿

Portraiture4最新简体中文li磨皮滤镜插件

茶色酒

Portraiture Portraiture4

前端图片最优化压缩方案

凉城

前端 图片处理 图片压缩 前端图片压缩

我的快速调优线上服务器CPU利用率通用办法,震惊面试官

KINDLING

Java cpu 服务器 性能调优 ebpf

支付对接常用的加密方式介绍以及java代码实现

京东科技开发者

Java 安全 哈希算法 加密算法 非对称加密算法

设计模式-工厂方法模式和抽象工厂模式

C++后台开发

数据结构 设计模式 后端开发 Linux服务器开发 C++开发

在这些工厂、农田、服务区,看到智能中国的草蛇灰线

脑极体

人工智能 华为 许昌

关于Zebec生态的改进提案,以及即将上线的 Nautilus 链

BlockChain先知

高校数据库/SQL教学用什么样的SQL工具?管理更方便,学习更轻松

雨果

数据库管理工具 :MySQL 数据库 SQL开发工具

重识Flutter 用于解决复杂滑动视窗问题的Slivers - part1

编程的平行世界

flutter 前端 an'droid

有关TCP协议,这是我看过讲的最清楚的一篇文章了!

程序员小毕

程序员 TCP 程序人生 计算机网络 架构师

谷歌用Bard打响了Chat GPT的第一枪,百度版Chat GPT 何时出炉?

GPU算力

Sugar BI 增强分析能力全场景解析

XxinQi

数据分析 可视化 BI 商务智能 预测模型

《数字经济全景白皮书》出海篇:选对路径下好棋,热点出海行业如何实现增长?

易观分析

数字化 经济 出海

Boom 3D免费电脑环绕音乐软件2023最新版下载

茶色酒

Boom 3D

ChatGPT集成之前,让我们复习一下即将过时的知识

newbe36524

搜索引擎; ChatGPT

小游戏内测|小游戏脱离微信运行在其它 App

Onegun

微信小程序 小游戏 小游戏开发 微信小程序-游戏

关于小程序游戏变现方式你还知道哪些?

没有用户名丶

前端开发 小程序游戏

100 行 shell 写个 Docker

vivo互联网技术

Docker Shell

【Rust学习】内存安全探秘:变量的所有权、引用与借用

京东科技开发者

spring rust slice 企业号 2 月 PK 榜 可变引用

十年老程序员:再见了Navicat,以后多数据库管理就看这款SQL工具

雨果

sql navicat 数据库管理工具

BIGO 如何做到夜间同时运行 2.4K 个工作流实例?

白鲸开源

spark 工作流调度 Apache DolphinScheduler 离线计算

可路由计算引擎实现前置数据库

石臻臻的杂货铺

数据库

CNStack 2.0:云原生的技术中台

阿里巴巴云原生

阿里云 云原生 技术中台

Top 5 OSSInsight 年度最佳 MLOps 开源工具

Jina AI

深度学习 开源框架 Jina MLOps OSSInsight.io

架构实战营 10 期 - 作业 6

炮仗

郑晔谈Moco框架的开发:写一个好的内部DSL,写一个表达性好的程序_Java_张龙_InfoQ精选文章