引入間接隔離變化(一)

作者: 張逸  來源: 博客園  發布時間: 2011-01-27 10:45  閱讀: 885 次  推薦: 0   原文鏈接   [收藏]  

  David Wheeler有一句名言:“計算機科學中的大多數問題都可以通過增加一層間接性來解決。”間接代表著迂回。世間沒有哪一條道路是完全筆直的。蜿蜒曲折的道路并非出于美的靈感,不過是因為我們需要繞開路途中的障礙罷了。

  我們在設計中遇到的最大障礙,無疑就是變化。若能御變化于實現之外,軟件開發就會變得美好。

  應對變化的要訣是隔離。設計者需要界定對象的不變部分與可變部分,然后將可變部分隱藏起來,即使發生了變化,也不會影響到外部。這就是封裝的含義。正如地殼核心的變化如此的狂暴與迅捷,但對于地面上生活的人類而言,幾乎微不可察。然而,一旦地殼的變化沖出地表,就會釀成天大的災難。變化對軟件系統造成的災難,并不亞于地震或者火山。封裝為對象內部的實現設定了一層隔離帶,將復雜變化的業務邏輯或者算法策略隱藏在對象之內。只要保證對象的接口不發生變化,調用者與對象內部的實現就可以單獨演化了。

  當我們發現一個對象需要依賴另一個不穩定的對象,同時,還需要執行復雜的交互邏輯時,就可以考慮引入一個新的對象來封裝這些邏輯,從而解除二者之間的耦合,隔離變化。Spring MVC中的ModelAndView對象扮演的正是這一角色。根據MVC模式,控制器需要將模型對象所持有的數據以及數據的變化呈現到視圖中。它通過尋找正確的視圖對象,完成頁面的展現。控制器承擔了這一職責,就意味它必須依賴于視圖對象。例如這樣的代碼:

 
public class CustomerController implements Controller {
@Override

public View handleRequest(
HttpServletRequst request,
HttpServletResponse response) throws Exception {
Map model
= new HashMap();
model.put(“customers”, getCustomerList());

return new InternalResourceView(”/WEB-INF/jsp/customerList.jsp”);
}
}

  View具體對象的創建,使得CustomerController與InternalResourceView緊緊地綁定起來,失去了靈活性,導致我們無法自由改變View的實現。作為一個靈活的MVC框架,顯然很難容忍這二者之間的強耦合。要打破這種耦合關系,就需要封裝尋找以及創建視圖的職責,并將這一職責放到合適的對象中。這正是引入ModelAndView類的緣由。Controller放心地將所有與View相關的職責轉移給ModelAndView,而它只需要悠閑地傳遞一個視圖名稱即可。

 
public class CustomerController implements Controller {
@Override

public ModelAndView handleRequest(
HttpServletRequst request,
HttpServletResponse response) throws Exception {
Map model
= new HashMap();
model.put(“customers”, getCustomerList());

return ModelAndView(”customerList”, model);
}
}

  通過字符串類型的名稱常量去尋找合適的視圖,而不是具體的View對象,就使得Controller沖破了View類型的約束,變得自在而開放。因為封裝的作用,Controller對象變得無知,然而,“無知者無畏”,它也不用害怕視圖呈現所發生的變化了。image   隔離變化的另一條途徑是尋覓對象的共性,對這些共性進行抽象。我們不必考慮對象實現細節的不同之處,只需要把握對象的共同特征,即可完成接口的定義。接口可以看做是對象的角色。Rebecca認為:“客戶訪問接口比訪問具體類要靈活得多,它們不需要知道具體實現,而只需明了接口中聲明的公共角色即可。”【注:Rebecca Wirfs-Brock《對象設計:角色、責任和協作》】角色代表一種功能或職責的扮演,它并非演員本身,只是形象化地以某種形態或語言來表現角色的喜怒哀樂而已。例如,我們需要在項目中指定規則以限定渲染的格式。這個規則可以是數據區間,只要數據在這個區間范圍之內,就應該設置為對應的格式;也可以是某種約束條件,當條件滿足時,以相應的格式渲染。從實現細節來看,區間與約束是迥然不同的兩種實現;可是從抽象的角度看,它們無疑扮演的都是同一種角色,那就是匹配器。只要規則匹配,就應該獲得正確的格式。image

 
public interface Matcher {
public boolean matches(Object value);
}


public class Range implements Matcher{
private double min;
private double max;
public Range(double min, double max) {
this.min = min;
this.max = max;
}

private boolean in(double data) {
//判斷data是否在此區間
}

public boolean matches(Object value) {
try {
return in((double)value);
}
catch (InvalidCastException) {
return false;
}
}
}


public class Constraint implements Matcher {
private String expected;
private boolean ignoreCase;
public Constraint(String expected) {
this.expected = expected;
ignoreCase
= true;
}


public Constraint(String expected, boolean ignoreCase) {
}

public boolean matches(Object value) {
if (ignoreCase) {
return expected.equalsIgnoreCase(value.toString());
}
else {
return expected.equals(value.toString());
}
}
}

  Matcher接口抽象了區間與約束的共性特征,使得二者在規則中能夠友好相處:

 
public class Rule {
public Rule(Matcher matcher, Format format){}
public Matcher getMatcher(){}
public void setMatcher(Matcher matcher){}
public Format getFormat(){}
public void setFormat(Format format){}
}

  如果需要更多的匹配器,只要實現Matcher接口,就可以放入Rule中,作為格式規則的一部分。這種包容變化的能力,正是抽象能夠提供的。

0
0
 
標簽:OO
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()