连贯接口(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 ...
随机推荐
- centos7安装python-pip
在使用centos7的软件包管理程序yum安装python-pip的时候会报一下错误: No package python-pip available. Error: Nothing to do 说没 ...
- (五) openwrt打包过程
标签(空格分隔): Makefile 本周是成胖子每周一博第六周,更好地阅读体验,请点击这里 前言 前面我们已经讲了openwrt编译的大部分过程,包括大致的编译步骤,ipk的编译等.今天是我这个系列 ...
- D_S 顺序栈的基本操作
// main.cpp #include <iostream> using namespace std; #include "Status.h" typedef in ...
- xargs -n1 -t
杀掉删除没有释放空间的进程 可以用如下命令,当然也可以写一个循环,不过这一条命令比循环简单 lsof |grep deleted |awk '{print $2}' |xargs -n1 -t ...
- Sample SecondarySort 浅析
示例文件: 100 99 100 98 100 56 100 78 20 100 30 100 20 50 30 50 30 60 20 80 需求:首先按第一个数字分组,组成按第二个数字排序. 解决 ...
- MAPR 开发环境搭建过程记录
我下载了MAPR 官方提供的virtualbox 和 vmware版本的sandbox进行试用. 开始试用了一会vmware版的,因为不太熟悉vmware的操作,而且vmplayer经常没有反应,后来 ...
- function [ binary,decimal ] = num2binary16( number )
function [ binary,decimal ] = num2binary16( number ) %The IEEE 754 standard specifies a binary16 as ...
- selenium向富文本框填写内容的几种方式
富文本框如果是iframe,则用下 1.先跳转到irame,dr.switchTo().frame(wtext); 然后用js JavascriptExecutor jsExecutor = (Jav ...
- 【转】Python中string的strip,lstrip,rstrip用法
Python中的strip用于去除字符串的首尾字符串,同理,lstrip用于去除左边的字符,rstrip用于去除右边的字符. 这三个函数都可传入一个参数,指定要去除的首尾字符. 需要注意的是,传入的是 ...
- 2016"百度之星"-资格赛
//本题要求:(Ar*A2...An)%p,亦即[(A1*A2*...An)/(A1*A2*...Ar-1)]%p,由于A1*A2...An乘积过大,无法求得相除所得的结果 //我们需要用到乘法逆元( ...