连贯接口(fluent interface)的Java实现及应用。
几年前在单元测试时使用mockito和junit(使用hamcrest提供的比较方法)的时候,就用到过这样类似的语法:
mockito:
when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");
junit:
assertThat(responseString, either(containsString("color")).or(containsString("colour")));
如果按照自然语言来理解,非常清晰:如第一个例子,当被mock的类的某个方法被调用时,先会抛出一个Runtime的异常 然后会返回一个“foo”字符串。
但是,如果按照以前我们学习的java的用法来思维则会很困惑:when().thenThrow().thenReturn() 这是神马意思?想半天你才会猜测着理解这是when()方法返回了某对象有thenThrow()方法,该方法被调用以后又返回了某对象,某对象有thenReturn()方法可以被调用。我一直把它作为一种让编程变得简单的语法糖,没有深究。
在前一段学着写写安卓,第三次发现这种用法,而且被大范围使用了:例如一个Dialog,你会看到这样的用法:
new AlertDialog.Builder(this)
.setIcon()
.setTitle()
.setPositiveButton()
这又跟我们平时的用法不大一样。以前都是new出一个实例来以后,在它的下方整齐的写n个set来设置它的属性。这又没有前面2个例子里的前后处理关系。所以又习惯性的把它当做了语法糖。
直到前一段开始看老马的《特定领域语言》这本书才知道这种用法是一种DSL,老马还给他起了一个名字 连贯接口(fluent interface)。
关于DSL,它最大的用处就是:将常见模式抽取出来,使之变成更加有可读性的描述方式。是的!有好的可读性:你之所以觉得正则表达式(另一种DSL)比一堆String解析代码难读,是你没有付出学习正则表达式的时间。当你掌握了一种DSL的规则后,你会觉得它更简便,更具有可读性,你要付出的是学习成本,而带来的则是效率的提高,与眼前的清爽。
上面的3个例子中,我们发现阅读更加简便,代码量也减少了很多。那么它是如何实现的呢?其实很简单,你可以把它看做是一种责任链模式的简写/变种。我们看一段示例代码,这段代码中,创建了一个AppoinetmentCalendarChained 的实例calendar。对calendar进行了链式操作:add().from().to().at()
public class CalendarDemoChained {
public static void main(String[] args) {
new CalendarDemoChained();
}
public CalendarDemoChained() {
Calendar fourPM = Calendar.getInstance();
fourPM.set(Calendar.HOUR_OF_DAY, 16);
Calendar fivePM = Calendar.getInstance();
fivePM.set(Calendar.HOUR_OF_DAY, 17);
AppointmentCalendarChained calendar =
new AppointmentCalendarChained();
calendar.add("dentist").
from(fourPM).
to(fivePM).
at("123 main street");
calendar.add("birthday party").at(fourPM);
displayAppointments(calendar);
}
private void displayAppointments(AppointmentCalendarChained calendar) {
for (Appointment a : calendar.getAppointments())
System.out.println(a.toString());
}
}
实现链式操作的类
public class Appointment {
private String _name;
private String _location;
private Calendar _startTime;
private Calendar _endTime;
public Appointment(String name) {
this._name = name;
}
public Appointment() {
}
public Appointment name(String name) {
_name = name;
return this;
}
public Appointment at(String location) {
_location = location;
return this;
}
public Appointment at(Calendar startTime) {
_startTime = startTime;
return this;
}
public Appointment from(Calendar startTime) {
_startTime = startTime;
return this;
}
public Appointment to(Calendar endTime) {
_endTime = endTime;
return this;
}
public String toString() {
return "Appointment:"+ _name +
((_location != null && _location.length() > 0) ?
", location:" + _location : "") +
", Start time:" + _startTime.get(Calendar.HOUR_OF_DAY) +
(_endTime != null? ", End time: " +
_endTime.get(Calendar.HOUR_OF_DAY) : "");
}
}
我们来看add from to 这些方法。每个方法调用后都把实例本身返回了进去,java支持匿名调用,这样就可以直接调用自身方法了,其实挺简单的。
当链式操作有顺序的时候(如某些操作必须有一些前置操作),就要多封装一些复杂逻辑了,老马的书里有很多丰富的思路介绍(第10,11,33,38,50章),但并不是非常详细,需要自己再拓展阅读。
btw,要多说2句的是,连贯接口只是DSL的一种,2个常见的测试相关框架已经在大量使用了。去年用的较多的Robotframework 使用的关键字驱动也是一种典型的DSL。多学习抽象的方法对自动化框架的设计应该说有很大好处。
参考书籍:《特定领域语言》
参考链接:
1.http://martinfowler.com/bliki/FluentInterface.html
2.http://www.ibm.com/developerworks/cn/java/j-eaed13/
3.http://jmock.org/oopsla2006.pdf
连贯接口(fluent interface)的Java实现及应用。的更多相关文章
- Fluent Interface(流式接口)
我最初接触这个概念是读自<<模式-工程化实现及扩展>>,另外有Martin fowler大师 所写http://martinfowler.com/bliki/FluentInt ...
- 基于JDK动态代理实现的接口链式调用(Fluent Interface)工具
什么是链式接口(Fluent Interface) 根据wikipedia上的定义,Fluent interface是一种通过链式调用方法来完成方法的调用,其操作分为终结与中间操作两种.[1] 下面是 ...
- Java 8函数式接口functional interface的秘密
Java 8函数式接口functional interface的秘密 2014年10月29日 17:52:55 西瓜可乐520 阅读数:3729 目录 [−] JDK 8之前已有的函数式接口 新定 ...
- Java链式方法 连贯接口(fluent interface)
有两种情况可运用链式方法: 第一种 除最后一个方法外,每个方法都返回一个对象 object2 = object1.method1(); object3 = object2.method2(); ob ...
- [JavaScript,Java,C#,C++,Ruby,Perl,PHP,Python][转]流式接口(Fluent interface)
原文:https://en.m.wikipedia.org/wiki/Fluent_interface(英文,完整) 转载:https://zh.wikipedia.org/wiki/流式接口(中文, ...
- JAVA 8 函数式接口 - Functional Interface
什么是函数式接口(Functional Interface) 其实之前在讲Lambda表达式的时候提到过,所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法. 这种类型的接 ...
- JAVA的核心概念:接口(interface)
JAVA的核心概念:接口(interface) 接口与类属于同一层次,实际上,接口是一种特殊的抽象类. 如: interface IA{ } public interface: 公开接口 与 ...
- Java基础-面向接口(interface)编程
Java基础-面向接口(interface)编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.接口的概念 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的“类 ...
- Java Naming and Directory Interface (JNDI) Java 命名和目录接口
https://www.oracle.com/technetwork/java/jndi/index.html Lesson: Overview of JNDI (The Java™ Tutorial ...
随机推荐
- Jmeter调试工具---Debug Sampler
一.Debug Sampler介绍: 使用Jmeter开发脚本时,难免需要调试,这时可以使用Jmeter的Debug Sampler,它有三个选项:JMeter properties,JMeter v ...
- Java 读取大文件方法
需求:实际开发中读取文本文件的需求还是很多,如读取两个系统之间FTP发送文件,读取后保存到数据库中或日志文件的数据库中保存等. 为了测试首先利用数据库SQL生成大数据文件. 规则是 编号|姓名|手机号 ...
- wireshark安装
原文链接地址:http://blog.csdn.net/holandstone/article/details/47026213 Wireshark下载地址:https://www.wireshark ...
- (一)openwrt源码目录概述
前言 这段时间总是在和openwrt打交道,之前也零零散散地写过一点,还是希望能有点体系.还记得我刚看到源代码的时候,觉得无从下手.我想从Makefile的整个执行过程入手,搞清楚编译源代码的几个小时 ...
- 通用cube refresh方案
通用cube refresh c# script 解决方法: 需要设置的变量如下: User::varcubename,User::varolapconnstr,User::varolapdbname ...
- 【温故而知新-Javascript】图片效果(图像震动效果、闪烁效果、自动切换图像)
1.当鼠标指针经过图像时图像震动效果 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " ...
- (转)对各种初始化函数的理解:OnInitDialog、InitInstance、InitApplication函数的理解
InitInstance和InitApplication 是 APP 类的初始化.而 OnInitDialog 是在 Dialog 类初始化时调用的函数. ★ 应用程序相关配置,一般在InitInst ...
- [转载]ExtJs4 笔记(5) Ext.Button 按钮
作者:李盼(Lipan)出处:[Lipan] (http://www.cnblogs.com/lipan/)版权声明:本文的版权归作者与博客园共有.转载时须注明本文的详细链接,否则作者将保留追究其法律 ...
- codeforces 442C C. Artem and Array(贪心)
题目链接: C. Artem and Array time limit per test 2 seconds memory limit per test 256 megabytes input sta ...
- jquery中的each()方法详解
each()方法能使DOM循环结构简洁,不容易出错.each()函数封装了十分强大的遍历功能,使用也很方便,它可以遍历一维数组.多维数组.DOM, JSON 等等在javaScript开发过程中使用$ ...