幾年前在單元測試時使用mockito和junit(使用hamcrest提供的比較方法)的時候,就用到過這樣相似的語法:html
mockito:java
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,你會看到這樣的用法:oop
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()ui
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