有两种情况可运用链式方法:
第一种 除最后一个方法外,每个方法都返回一个对象
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