Java新手的跌倒日記 - Tell Don't Ask?
Wed, 26 Dec 2007 13:35:42 +0800最近在MockObjects網站上看到的文章:
Mock Objects: Tell Don't Ask and Mock Objects
簡單地說,就是讓物件在做判斷決策時,只倚賴內部的資訊或是透過參數傳進來的資訊,而不倚賴其他物件所有的資訊。透過這樣的設計,可以讓物件之間的耦合降低,同時也更容易做單元測試。
作者舉了一個極端的反例:像 "object.getPart().getSubpart().getAttribute()" 這樣的敘述如果在程式中出現的話,因為耦合了隔鄰的物件,同時又耦合了隔鄰的隔鄰物件,結果就很難用Mock Object來進行單元測試了。
解決的辦法,是透過refactoring重新設計....不知道怎麼做耶???作者說在隔鄰的物件新增方法???
我嘗試了一下,應該可以透過Eclipse的「產生委派方法」來實現???不是很確定,不過動手試了一下:
package com.fillano; public class SubPart { private String attrib; public String getAttrib() { return attrib; } public void setAttrib(String attrib) { this.attrib = attrib; } }
package com.fillano; public class Part { private SubPart subPart; public Part () { subPart = new SubPart(); } public SubPart getSubPart() { return subPart; } public void setSubPart(SubPart subPart) { this.subPart = subPart; } }
package com.fillano; public class Product { private Part part; public Product () { part = new Part(); } public Part getPart() { return part; } public void setPart(Part part) { this.part = part; } }
這樣,就可以重現作者描述的train-wreck:
Product pd = new Product(); pd.getPart().getSubPart().setAttrib("an attrib"); System.out.println(pd.getPart().getSubPart.getAttrib());
透過在Product與Part類別新增委派方法,可以讓Product直接get/set SubPart的attrib:
package com.fillano; public class Part { private SubPart subPart; public Part() { subPart = new SubPart(); } public String getAttrib() { return subPart.getAttrib(); } public void setAttrib(String attrib) { subPart.setAttrib(attrib); } public SubPart getSubPart() { return subPart; } public void setSubPart(SubPart subPart) { this.subPart = subPart; } }
接著可以在Product類別中加入更多委派方法:
package com.fillano; public class Product { private Part part; public Product() { part = new Part(); } public String getAttrib() { return part.getAttrib(); } public SubPart getSubPart() { return part.getSubPart(); } public void setAttrib(String attrib) { part.setAttrib(attrib); } public void setSubPart(SubPart subPart) { part.setSubPart(subPart); } public Part getPart() { return part; } public void setPart(Part part) { this.part = part; } }
這樣似乎透過委派方法就可以去掉train-wreck的程式碼:
Product pd = new Product(); pd.setAttrib("an attrib"); System.out.println(pd.getAttrib());
說實話,這並不是refactoring,我也沒有改進物件之間的耦合。想了一下,有一個原因是設計太簡單,其實無法反映出tell don't ask style的精神。
2008-1-25 更新
根據在java world看到的文章,我這樣做並不是委派方法,而是request forwarding與composition。他認為真正的委派方法中,物件會把自己當作委派方法的參數傳遞給執行委派的物件。
2008-1-30 更新
請看續集:Java新手的跌倒日記 - 再看 Tell don't ask 及委派方法,我把問題重新想了一下....