學習 Design Patterns[14][16]後,可以讓我們以另一個不同的觀點來重新評估已編 寫的程式,讓軟體設計與開發時能具有可再利用(Reusable)與擴充容易(Extensible)的功 能。要設計出可再利用的物件導向軟體時,必須先歸納出適當的物件,分解成大小合宜 的類別,定義類別介面和繼承關係,並建立彼此之間的關連。許多物件導向系統裡面都 有一再出現的類別互動模式,這些模式不僅解決了特定問題,同時也讓物件導向系統更 富彈性、更具可再利用的性質。
利用 Java 語言撰寫程式時,常常會用到 Java 提供的類別函式庫,但是 Design Patterns 並不是類別函式庫,Design Patterns 比類別函式庫更具有一般性概念的味道,
類別函式庫是用來當作零組件的程式;而 Design Patterns 則是構思要如何組合零組 件、如何結合不同的零組件以發揮最大的功能。藉由過去成功的經驗引領出新的設計,
不必再苦思解決方案,就能馬上把 Design Patterns 套用上去。下面一小節開始將介紹 通訊模組內所使用到的 Design Patterns。
4.3.1 Façade Pattern
Façade Pattern 要求一個子系統的外部與其內部的通訊必須通過一個統一的門面 (Façade)物件進行。Façade Pattern 的門面類別將使用端與子系統的內部複雜性分隔 開,使得使用端只需要與門面物件打交道。
使用 Façade Pattern 的時機:
1. 為複雜子系統提供一個簡單介面-子系統往往因為不斷演化而變得越來越複 雜,使用 Façade Pattern 可以使得子系統更具可複用性。
2. 子系統的獨立性-引入 Façade Pattern 將一個子系統與它的使用端以及其它的 子系統分離,可以提高子系統的獨立性和可攜性。
3. 層級化結構-在建構一個層級化的系統時,可以使用 Façade 定義系統每一層的
入口。如果層與層之間是相互依賴的,則可以限定它們僅通過 Facade 進行溝通,
從而簡化層與層之間的依賴關係。
4.3.2 Bridge Pattern
Bridge Pattern 的目的是要將「功能的類別階層」和「實作的類別階層」分成兩個 獨立的類別階層。如果此 2 類別階層區隔清楚,就可以分別擴充兩邊的類別階層。如果
impl : DisplayImpl open()
4.3.3 Composite Pattern
對容器和內容物一視同仁,建立遞迴結構的 Design Pattern 就是 Composite Pattern。容器裡面可以是內容物,也可以是容器;而這個小一號的容器裡面還可以再放 更小一號的容器…,如此可以建立出以大套小的遞迴結構。
在 SECS-II 中所定義的 List Item 就好比是一個容器;而每個 List Item 後面所接的 Data Item(如:Integer Item、Boolean Item…等)代表的則是內容物。List Item 內可以放
Data Item,也可以放 List Item。以圖 4.5 中的 S4F27 這個訊息架構來說,第一個 List Item 中放了兩個內容物,其一是名為 EQNAME 的 Data Item,其二又是一個 List Item,而 此 List Item 內則放了 11 個 Data Item,分別是 TRLINK、TRPORT…等。因此在 SECS-II 的訊息物件架構上採用了 Composite Pattern,可以使得在對訊息物件做處理時變得簡 單許多。
圖 4.8 是參考[16]中的範例程式,利用電腦檔案系統中,資料夾與檔案之間的關係 來說明 Composite Pattern 中各物件的關係。父類別 Entry 是一個抽象類別,由其子類 別容器(Directory 類別)、內容(File 類別)來實作。由圖 4.8 的類別圖中可看出 Directory 類別與 Entry 類別有 aggregation 的關係,其表示 Directory 類別是由 Entry 物件的子類 別-Directory 類別、File 類別所組成,而 Directory 類別又是 Entry 類別的子類別;也 就是說,Directory 物件中可能又包含了 Directory 物件,容器中可能有小一號的容器的 意思,而構成遞迴結構。
利用同樣的架構應用到 SECS-II 的訊息架構中,再配合下一節將介紹的 Visitor Pattern,可以使得整個通訊模組在訊息編譯與解譯上更加清晰。詳細應用將在 4.3.5 節 中說明。
File name : String size : int
File(name : String, size : int) getName() : String
getSize() : int
printList(prefix : String) : void Main
main()
Entry getName() : String getSize() : int
add(entry : Entry) : Entry printList() : void
printList(prefix : String) : void toString() : String
Directory name : String
directory : java.util.Vector = new Vector () Directory(name : String)
getName() : String getSize() : int
add(entry : Entry) : Entry printList(prefix : String) : void
圖 4.8 Composite Pattern 類別圖
4.3.4 Visitor Pattern
Visitor Pattern 是把「資料結構」和「處理」兩者分開的 Design Pattern。SECS-II 中所規範的訊息架構就是一種「資料結構」,而對訊息所做的編譯與解譯的動作就相當 於是對這個資料結構所做的「處理」。若是能將「資料結構」和「處理」分開,當日後 需要增加或是修改「處理」時,「資料結構」依舊保持不變,如此可以保證資料結構的 完整性。
圖 4.9 中的 NewClass_Element 類別與 NewClass_Visitor 類別皆為抽象類別,透 過子類別實作 visitor()與 accept()方法,達到「應用」能在「資料結構」中交互穿梭。
NewClass
accept() NewClass() NewClass_Concrete_Visitors_A
visit(element : NewClass) : void NewClass_Concrete_Visitors_A()
NewClass_Element
accept()
NewClass_Element() NewClass_Visitor
visit(element : NewClass) : void NewClass_Visitor()
abstract Class
圖 4.9 Visitor Pattern 類別圖
4.3.5 Design Patterns 在通訊模組上之應用
Façade Pattern 在通訊模組內的應用可以參考 4.4.1 節的介紹,其目的是要提供控 制器使用通訊模組的窗口,控制器需透過 Façade 類別才可存取通訊模組內的訊息物 件。Bridge Pattern 在通訊模組的應用可以參考 5.2.3 節及圖 5.4 應用 Bridge Pattern 的時間調整類別圖,其目的是要把控制器程式的功能層與實作層分開,達到功能與實作 擴充分開的目的。
本節主要說明 Composite Pattern 與 Visitor Pattern 在通訊模組上的應用。由 4.2.1 節中可知一個訊息是由 List 與 Data Item 所組成,而從表 4.4 中可得知每個訊息對應 List 與 Data Item 的 Format Code。通訊模組的訊息架構部份,就是利用訊息中的樹狀 結構-List 與 Data Item 所組成而建立類似 Composite Pattern 的架構,如圖 4.10 右半 部所示。Item 類別為抽象類別,List Item 物件則是一個由 Item 物件所構成的,所以從 類別圖中可看出,List Item 類別與 Item 類別有 aggregation 的關係。依照表 4.4 所規 範的資料型態分別建立相對應的資料型態類別,如:String Item、Integer Item、Boolean Item…等類別,而這些資料型別類別中都有一個表示 Format Code 的屬性,之後的應用 也都是利用 Format Code 來分辨 Data Item 的資料型態。所以通訊模組中訊息物件的架 構就是利用 Composite Pattern 來建立。
有 了 依 照 Composite Pattern 架 構 所 建 立 的 訊 息 物 件 後 , 對 於 訊 息 的 編 譯 (Encoder)、解譯(Decoder)等「應用」,則可以使用 Visitor Pattern 來實作。如圖 4.10 左半部所示,有幾個應用就建立幾個 Visitor 物件,如:EncoderVisitor 類別是實作如何 將訊息物件編譯為文字字串、Encoder2ByteVisitor 類別則是實作如何將訊息物件編譯為 位元組、DecoderVisitor 類別則是實作如何將位元組解譯為訊息物件。
Acceptor
(from Item) u_IntegerItem
(from Item) u_LongItem
(from Item) u_ShortItem
(from Item) Encoder2ByteVisitor
(from Vi si tor)
EncoderVisitor (from Vi sitor)
Visitor
visit(file : secsii.Item.BinaryItem) : void visit(file : secsii.Item.BooleanItem) : void visit(file : secsii.Item.CharItem) : void visit(file : secsii.Item.DoubleItem) : void visit(file : secsii.Item.FloatItem) : void visit(file : secsii.Item.LongItem) : void visit(file : secsii.Item.ByteItem) : void visit(file : secsii.Item.ShortItem) : void visit(file : secsii.Item.IntegerItem) : void visit(file : secsii.Item.u_LongItem) : void visit(file : secsii.Item.u_ByteItem) : void visit(file : secsii.Item.u_ShortItem) : void visit(file : secsii.Item.u_IntegerItem) : void visit(file : secsii.Item.StringItem) : void visit(directory : secsii.Item.ListItem) : void
(from Visitor)
Message (from secsii ) S4F19
利用 Visitor Pattern 將資料結構與處理分開,一個 Visitor 物件可以對整個通訊模組 的訊息物件做編譯與解譯,而不需要每個訊息物件都需去編寫一再重複的程式碼。對於 編譯、解譯動作的修改,也只需更改 Visitor 物件即可,而不需要更動全部的訊息物件。
因為使用了 Composite Pattern 的資料架構,可以建構出複雜的訊息架構,只要資料架 構完成,Visitor 物件就可穿梭在資料架構中作動。