Java链式方法 连贯接口(fluent interface)
有两种情况可运用链式方法:
第一种 除最后一个方法外,每个方法都返回一个对象
object2 = object1.method1();
object3 = object2.method2();
object3.method3();
以上三行代码等价于如下链式方法
object1.method1().method2().method3();
第二种 调用同一个对象的多个方法
user.setName(String name);
user.setPassword(String password);
user.setRole(String role);
以上三行代码等价于如下链式方法
user
.setName(String name)
.setPassword(String password)
.setRole(String role);
示例:
使用chaining:
package com.dxz.base; public class Persion { private int id; private String name; private String phoneNumber; private String address; public Persion() { } public Persion setId(int id) { this.id = id; return this; } public Persion setName(String name) { this.name = name; return this; } public Persion setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; return this; } public Persion setAddress(String address) { this.address = address; return this; } public Persion printId() { System.out.println(this.id); return this; } public Persion printName() { System.out.println(this.name); return this; } public Persion printPhoneNumber() { System.out.println(this.phoneNumber); return this; } public Persion printAddress() { System.out.println(this.address); return this; } }
package com.dxz.base; public class Test { public static void main(String[] args) { /*Persion persion1 = new Persion(); persion1.setId(3); persion1.setName("John"); persion1.setPhoneNumber("1111111"); persion1.setAddress("US"); persion1.printId(); persion1.printName(); persion1.printPhoneNumber(); persion1.printAddress();*/ Persion persion1 = new Persion(); persion1.setId(3).setName("John") .setPhoneNumber("1111111").setAddress("US"); persion1.printId() .printName() .printPhoneNumber() .printAddress(); } }
结果:
3 John 1111111 US
直到前一段开始看老马的《特定领域语言》这本书才知道这种用法是一种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