設想這樣乙個需求,我們需要為自己的框架提供乙個負責排序的元件。目前需要實現的是氣泡排序演算法和快速排序演算法,根據「面向介面程式設計」的思想,我們可以為這些排序演算法提供乙個統一的介面isort,在這個介面中有乙個方法sort(),它能接受乙個object陣列引數。對陣列進行排序後,返回該陣列。介面的定義如下:
public inte***ce isort
其類圖如下:
然而一般對於排序而言,排列是有順序之分的,例如公升序,或者降序,返回的結果也不相同。最簡單的方法我們可以利用if語句來實現這一目的,例如在quicksort類中:
public class quicksort:isort
public void sort(ref object besorted)
else
} }
當然,我們也可以將string型別的sorttype定義為列舉型別,減少出現錯誤的可能性。然而仔細閱讀**,我們可以發現這樣的**是非常僵化的,一旦需要擴充套件,如果要求我們增加新的排序順序,例如字典順序,那麼我們面臨的工作會非常繁重。也就是說,變化產生了。通過分析,我們發現所謂排序的順序,恰恰是排序演算法中最關鍵的一環,它決定了誰排列在前,誰排列在後。然而它並不屬於排序演算法,而是一種比較的策略,後者說是比較的行為。
如果仔細分析實現isort介面的類,例如quicksort類,它在實現排序演算法的時候,需要對兩個物件作比較。按照重構的做法,實質上我們可以在sort方法中抽取出乙個私有方法compare(),通過返回的布林值,決定哪個物件在前,哪個物件在後。顯然,可能發生變化的是這個比較行為,利用「封裝抽象」的原理,就應該為該行為建立乙個專有的介面icompare,然而分別定義實現公升序、降序或者字典排序的類物件。
我們在每乙個實現了isort介面的類建構函式中,引入icompare介面物件,從而建立起排序演算法與比較演算法的弱耦合關係(因為這個關係與抽象的icompare介面相關),例如quicksort類:
public class quicksort:isort
public void sort(ref object besorted) }//
實現略
} }
最後的類圖如下:
通過對比較策略的封裝,以應對它的變化,顯然是stategy模式的設計。事實上,這裡的排序演算法也可能是變化的,例如實現二叉樹排序。由於我們已經引入了「面向介面程式設計」的思想,我們完全可以輕易的新增乙個新的類binarytreesort,來實現isort介面。對於呼叫方而言,isort介面的實現,同樣是乙個strategy模式。此時的類結構,完全是乙個對擴充套件開發的狀態,它完全能夠適應類庫呼叫者新需求的變化。
再以petshop為例,在這個專案中涉及到訂單的管理,例如插入訂單。考慮到訪問量的關係,petshop為訂單管理提供了同步和非同步的方式。顯然,在實際應用中只能使用這兩種方式的其中一種,並由具體的應用環境所決定。那麼為了應對這樣一種可能會很頻繁的變化,我們仍然需要利用「封裝變化」的原理,建立抽象級別的物件,也就是iorderstrategy介面:
public inte***ce iorderstrategy
然後定義兩個類ordersynchronous和orderasynchronous。類結構如下:
在petshop中,由於使用者隨時都可能會改變插入訂單的策略,因此對於業務層的訂單領域物件而言,不能與具體的訂單策略物件產生耦合關係。也就是說,在領域物件order類中,不能new乙個具體的訂單策略物件,如下面的**:
iorderstrategy orderinsertstrategy = new ordersynchronous();
在martin fowler的文章《ioc容器和dependency injection模式》中,提出了解決這類問題的辦法,他稱之為依賴注入。不過由於petshop並沒有使用諸如sping.net等ioc容器,因此解決依賴問題,通常是利用配置檔案結合反射來完成的。在領域物件order類中,是這樣實現的:
public class order
}在配置檔案web.config中,配置如下的section:
這其實是一種折中的service locator模式。將定位並建立依賴物件的邏輯直接放到物件中,在petshop的例子中,不失為一種好方法。畢竟在這個例子中,需要依賴注入的物件並不太多。但我們也可以認為是一種無奈的妥協的辦法,一旦這種依賴注入的邏輯增多,為給程式者帶來一定的麻煩,這時就需要乙個專門的輕量級ioc容器了。
寫到這裡,似乎已經脫離了「封裝變化」的主題。但事實上我們需要明白,利用抽象的方式封裝變化,固然是應對需求變化的王道,但它也僅僅能解除呼叫者與被呼叫者相對的耦合關係,只要還涉及到具體物件的建立,即使引入了工廠模式,但具體的工廠物件的建立仍然是必不可少的。那麼,對於這樣一些業已被封裝變化的物件,我們還應該充分利用「依賴注入」的方式來徹底解除兩者之間的耦合。
封裝變化(三)
設想這樣乙個需求,我們需要為自己的框架提供乙個負責排序的元件。目前需要實現的是氣泡排序演算法和快速排序演算法,根據 面向介面程式設計 的思想,我們可以為這些排序演算法提供乙個統一的介面isort,在這個介面中有乙個方法sort 它能接受乙個object陣列引數。對陣列進行排序後,返回該陣列。介面的定...
封裝變化 Part Three
filed under design pattern bruce zhang 6 35 pm 設想這樣乙個需求,我們需要為自己的框架提供乙個負責排序的元件。目前需要實現的是氣泡排序演算法和快速排序演算法,根據 面向介面程式設計 的思想,我們可以為這些排序演算法提供乙個統一的介面isort,在這個介面...
封裝變化(一)
軟體設計最大的敵人,就是應付需求不斷的變化。變化有時候是無窮盡的,於是專案開發就在反覆的修改 更新中無限期地延遲交付的日期。變化如懸在頭頂的達摩克斯之劍,令許多軟體工程專家一籌莫展。正如無法找到解決軟體開發的 銀彈 要徹底將變化扼殺在搖籃之中,看來也是不可能完成的任務。那麼,積極地面對 變化 方才是...