從Java類庫看設計模式(Composite,Strategy,Iterator)

發表于:2007-05-25來源:作者:點擊數: 標簽:javaStrategComposite類庫設計模式
從 Java 類庫看設計模式 (Composite,Strategy,Iterator) 出處 IBM DW 劉武東 (wdliu@chinaren.com) 2002 年 1 月 本文除了還會介紹一個結構型的Composite模式之外,還會有兩個行為模式登

Java類庫看設計模式 (Composite,Strategy,Iterator)


出處 IBM DW 
劉武東 (wdliu@chinaren.com)
2002 年 1 月


本文除了還會介紹一個結構型的Composite模式之外,還會有兩個行為模式登場。實際上在前面的內容中,我們已經接觸到行為模式了:Observer和Command就是兩個典型的行為模式。行為模式更多的注重于算法和對象建間職責的分配,也就是說,它會更多的關注于這個模式系統之類的各對象協作間的語義,以及在對象間進行通訊的流控制。

Composite模式 

毫無疑問的,AWT中的Component-Container體系就是一個很好的Composite模式的例子。Container繼承于Component,而Container中有可以包含有多個Component,因為Container實際上也是Component,因而Container也可以包含Container。這樣通過Component-Container結構的對象組合,形成一個樹狀的層次結構。這也就是Composite模式所要做的。

Composite模式是為了簡化編程而提出的,一般的在編程的時候,如果嚴格的區分Component和Container的話,有時候會帶來許多不便,而且這些往往是沒有必要的。比如,我要在一個Container中放置一個Component,我并不需要知道這個Component到底是一個Container,或者就是一個一般的Component,在父級容器中所要做的,只是記錄一個Component的引用,在需要的時候調用Component的繪制方法來顯示這個Component。當這個Component確實是一個Container的時候,它可以通過Container重載后的繪制方法,完成對這個容器的顯示,并把繪制消息傳遞給到它的子對象去。也就是說,對一個父級容器而言,它并不不關心,其子對象到底是一個Component,還是一個Container。它需要將Component和Container統一對待。


圖十一:Composite模式的類圖 

Composite模式比較簡單,實現起來也不復雜,但是有一定的局限性。比如,在處理樹的時候,我們往往需要處理三類對象:子樹,頁節點和非頁節點。而在Composite模式中對于子樹和非葉節點的區分并不明顯,而是把他們合成為一個Composite對象了。而且在GOF給出的Composite的模式中,對于添加,刪除子節點等屬于Composite對象的的方法,是放在了Component對象中的,這雖然在實現的時候可以區分開來,但容易造成一些概念上的誤解。

由上所敘,我們可以提出一個改進了的Composite模式,引入子樹對象,從而將子樹和非葉節點分開,如下圖所示: 





圖十二:Composite模式的一種變體 

雖然將Composite從Component類層次中分離出來,但并沒有損害Composite模式的內涵。這樣做不一定就會比上面的那個要好,各有不同的應用,不過有時候用這樣的方法來處理子樹要容易些,概念上也更為清晰。


下面的代碼,給出了一個Composite模式簡單的Java實現: 

public abstract class Component{
public abstract void operation(); 
public void add(Component component){};
public void remove(Component component){}; 
}

import java.util.*;
public class Composite extends Component{
String name;
ArrayList children = new ArrayList();
public Composite(String name){
this.name = name;
}
public void add(Component component){
children.add(component);
}
public void remove(Component component){
children.remove(component);
}
public void operation(){
System.out.println(name);
Iterator iterator = children.iterator();
while(iterator.hasNext()){
Component child = (Component)iterator.next();
child.operation();
}
}
}

public class Leaf extends Component{
String name;
public Leaf(String name){
this.name = name;
}
public void operation(){
System.out.println(name);
}
}




Strategy模式 

Strategy模式主要用來將算法實現從類中分離出來,并封裝在一個單獨的類中。更簡單的說,對象與其行為(behaviour)這本來緊密聯系的兩部分被解耦,分別放在了兩個不同的類中。這使得對同一個行為,可以方便的在任何時候切換不同的實現算法。而通過對策略的封裝,為其提供統一的接口,也可以很容易的引入新的策略。



AWT的LayoutManager,是Strategy模式的一個例子。對于GUI而言,每個組件(Component)在容器中(Container)的排放是需要遵循一定的算法的。通常的方法是使用絕對坐標,就像VB,Delphi之類的工具所作的那樣,記錄每個組件在容器中的位置。這當然會帶來一些問題,比如在窗體縮放的時候,就需要手工編碼改變組件的大小和位置,以使得原來的比例得以保存。而在AWT中,引入了布局管理器(LayoutManager)的概念,使得布局的方法大大豐富,編碼過程也變得簡單。



一個容器,比如Applet,Panel等,僅僅記錄其包含的組件,而布局管理器中封裝了對容器中組件進行布局的算法,具體地說,就是指明容器中組件的位置和尺寸的大小。通過布局管理器,你只需要確定想放置的組件間的相對位置即可,這一方面簡化編碼,另一方面也有助于實現軟件的平臺無關性。







圖十三:AWT中的容器和布局管理器的關系 

每一個容器均有一個布局管理器,當容器需要布置它的組件時,它調用布局管理器的方法布置容器內的組件。LayoutManager2繼承于LayoutManager,提供更為細致的布局功能,它可以讓布局管理器為組件加上約束條件已確定組件如何被布置。例如,為了確定組件被擺放在邊框內的位置,BorderLayout在它的組件上加上方向指示。


特別的,通過實現LayoutManager或者LayoutManager2接口,可以很容易實現自定義的布局策略。

回到模式的話題上來,如果有幾個很相似的類,其區別僅僅是在個別行為上的動作不同,這時候就可以考慮使用Strategy模式。這樣,通過策略組合,將原來的多個類精簡為一個帶有多個策略的類。這很符合OO設計的原則:找到變化的部分,并將其封裝起來!Strategy模式同樣的為子類繼承提供了一個好的替代方案,當使用繼承機制的時候,行為的改變是靜態的,你指能夠改變一次--而策略是動態的,可以在任何時候,切換任何次數。更為重要的是,策略對象可以在不同的環境中被不同的對象所共享。以布局管理器為例,雖然每一個容器只有一個布局管理器,但是一個布局管理器可以為多個容器工作。






圖十四:Strategy模式的類圖 

Strategy模式也有一些缺點,比如,應用程序必須知道所有的策略對象,并從中選者其一。而且在策略對象被使用的時候,它和Context對象之間通常是緊耦合的,Context對象必須為策略對象提供與具體算法相關的數據或者其它的東西,而這些數據的傳遞可能并不能夠風裝載抽象地策略類中,因為并不是所有的算法都會需要這些數據的。另外,因為策略對象通常由應用程序所創建,Context對象并不能夠控制Strategy的生命期,而在概念上,這個策略應該從屬于Context對象,其生命期不應該超出Context的范圍對象。


通常的,Strategy很容易和Bridge模式相混淆。確實,他們有著很相近的結構,但是,他們卻是為解決不同的問題而設計的。Strategy模式注重于算法的封裝,而Bridge模式注重于分離抽象和實現,為一個抽象體系提供不同的實現。


Iterator 模式 

Iterator模式用來規格化對某一數據結構的遍歷接口。


JDK中在Collection Framework中引入了Iterator接口,提供對一個Collection的遍歷。每一個Collection類中都定義有從Collection接口中繼承而來的iterator()方法,來得到一個Iterator對象,我們稱之為遍歷器,Iterator接口很簡單:

hasNext():用來判斷在遍歷器中是否還有下一個元素。

next():返回遍歷器中的下一個元素。

remove():在被遍歷的Collection類中刪除最后被返回的那個對象。

我們就以最為常用的Vector為例,看看在Collection Framework中,Iterator模式是如何被實現的。在此之前,我們需要先了解一些Vector和Collection 
Framework的結構。

Collection接口作為這個Framework的基礎,被所有其它的集合類所繼承或者實現。對Collection接口,有一個基本的實現是抽象類AbstractCollection,它實現了大部分與具體數據結構無關的操作。比如判斷一個對象是否存在于這個集合類中的contains()方法: 

public boolean contains(Object o) {
Iterator e = iterator();
if (o==null) {
while (e.hasNext())
if (e.next()==null)
return true;
} else {
while (e.hasNext())
if (o.equals(e.next()))
return true;
}
return false;
}

而這其中調用的iterator()方法是一個抽象方法,有賴于具體的數據結構的實現。但是對于這個containers()方法而言,并不需要知道具體的Iterator實現,而只需要知道它所提供的接口,能夠完成某類任務既可,這就是抽象類中抽象方法的作用。其它的在AbstractCollection中實現的非抽象方法,大部分都是依賴于抽象方法iterator()方法所提供的Iterator接口來實現的。這種設計方法是引入抽象類的一個關鍵所在,值得仔細領悟。


List接口繼承Collection接口,提供對列表集合類的抽象;對應的AbstractList類繼承AbstractCollection,并實現了List接口,作為List的一個抽象基類。它對其中非抽象方法的實現,也大抵上與AbstractCollection相同,這兒不再贅敘。



而對應于Collection的Iterator,List有其自己的ListIterator,ListIterator繼承于Iterator,并添加了一些專用于List遍歷的方法:

boolean hasPrevious():判斷在列表中當前元素之前是否存在有元素。

Object previous():返回列表中當前元素之前的元素。

int nextIndex():

int previousIndex():

void set(Object o):

void add(Object o):

ListIterator針對List,提供了更為強勁的功能接口。在AbstractList中,實現了具體的iterator()方法和listIterator()方法,我們來看看這兩個方法是如何實現的: 


public Iterator iterator() {
return new Itr(); //Itr是一個內部類
}
private class Itr implements Iterator {
int cursor = 0;//Iterator的計數器,指示當前調用next()方法時會被返回的元素的位置
int lastRet = -1;//指示剛剛通過next()或者previous()方法被返回的元素的位置,-1
//表示剛剛調用的是remove()方法刪除了一個元素。

//modCount是定義在AbstractList中的字段,指示列表被修改的次數。Iterator用//這個值來檢查其包裝的列表是否被其他方法所非法修改。
int expectedModCount = modCount;

public boolean hasNext() {
return cursor != size();
}
public Object next() {
try {
//get方法仍然是一個抽象方法,依賴于具體的子類實現
Object next = get(cursor);
//檢查列表是否被不正確的修改
checkForComodification();
lastRet = cursor++;
return next;
} catch(IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
//同樣remove(int)也依賴于具體的子類實現
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch(IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}


這兒的設計技巧和上面一樣,都是使用抽象方法來實現一個具體的操作。抽象方法作為最后被實現的內容,依賴于具體的子類。抽象類看起來很像是一個介于接口和子類之間的一個東西。

從設計上來講,有人建議所有的類都應該定義成接口的形式,這當然有其道理,但多少有些極端。當你需要最大的靈活性的時候,應該使用接口,而抽象類卻能夠提供一些缺省的操作,最大限度的統一子類。抽象類在許多應用框架(Application 
Framework)中有著很重要的作用。例如,在一個框架中,可以用抽象類來實現一些缺省的服務比如消息處理等等。這些抽象類能夠讓你很容易并且自然的把自己的應用嵌入到框架中去。而對于依賴于每個應用具體實現的方法,可以通過定義抽象方法來引入到框架中。 


其實在老版本的JDK中也有類似的概念,被稱為Enumeration。Iterator其實與Enmeration功能上很相似,只是多了刪除的功能。用Iterator不過是在名字上變得更為貼切一些。模式的另外一個很重要的功用,就是能夠形成一種交流的語言(或者說文化)。有時候,你說Enumeration大家都不明白,說Iterator就都明白了。


小結: 

這部分介紹了三個模式:Composite,Strategy和Iterator。Composite是一個結構性的模式,用來協調整體和局部的關系,使之能夠被統一的安排在一個樹形的結構中,并簡化了編程。Strategy模式與Bridge模式在結構上很相似,但是與Bridge不同在于,它是一個行為模式,更側重于結構的語義以及算法的實現。它使得程序能夠在不同的算法之間自由方便的作出選擇,并能夠在運行時切換到其他的算法,很大程度上增加了程序的靈活性。Iterator模式提供統一的接口操作來實現對一個數據結構的遍歷,使得當數據結構的內部算法發生改變時,客戶代碼不需要任何的變化,只需要改變相應的Iterator實現,就可以無縫的集成在原來的程序中。


參考資源: 

設計模式:可復用面向對象軟件的基礎 機械工業出版社 

Java2類庫增補版 機械工業出版社 

Java2圖形設計:AWT卷 機械工業出版社 

可視化面向對象建模技術 北京航天航空工業大學出版社 

DK1.3源代碼 

UML用戶指南 機械工業出版社 



關于作者 

劉武東:武漢大學計算機學院2001級研究生。研究方向:可重用組件技術,設計模式。


原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97