2013年6月11日 星期二

物件導向程式設計與設計模式



物件導向程式設計方式在程式設計上的一種典範,相較於傳統將程式分為資料結構+演算法的程序導向程式設計方式,物件導向程式設計將程式看成如同真實世界的一個物件個體,在真實世界中每一物件個體都包含了它的特徵與行為,而物件導向程式設計便是將這種觀念與程式設計結合起來。程序導向程式設計中,所有的需求都是屬於一種資料的處理,所以必須要先設計好資料結構,再設計處理資料的運算邏輯,而物件導向程式設計方式卻是將程序導向程式設計概念中的結構與演算法加以混和,並依照實際需求分成不同的物件,並且去處理物件間彼此的特徵與行為,並藉由物件間的互動去玩成設計需求,結論來說物件導向程式設計與程序導向程式設計相比有以下三個特性:
一、封裝性(Encapsulation):物件導向程式設計方式以現實世界的物件為基礎,所以將物件本身的屬性變數與相關的行為邏輯程序都包裹在物件本身,程式的執行被視為是物件屬性的異動與物件方法的呼叫,程序導向程式設計則是將所有資料變數獨立出來,而將程式視為資料異動的流程。
二、繼承性(Inheritance):程序導向程式利用函數呼叫去達到程式的再利用性,新的函數可以藉由組合呼叫之前設計的舊函數來建置新的功能,而在物件導向的程式設計則利用現實世界生物繁衍的繼承觀念引入到程式設計當中,繼承的子物件不僅擁有父物件的行為,還可以繼承父物件的屬性資料,所以讓程式的可再利用性大大的增加。
三、多型(Polymorphism):由於物件導向的程式設計的封裝與繼承特性,所以程式可以針對父物件去進行處理與行為呼叫,而在程式執行的時候只要是繼承父物件的任何子物件都可以被執行,而不比理會這一個子物件內部的行為,這一個特性可以增加程式的可擴充性。讓程式可以透過相同的物件呼叫去使用新增加的功能。
物件導向程式設計方式與現實世界的觀念較相近,所以在處理一些現實人與人之間互動行為的設計需求比較容易,而且也能有較好的擴充性,所以本研究是用物件導向程式設計做為系統設計的設計方式。
雖然物件導向程式設計可以增加可維護性與可再利用性,但是不良的物件導向程式設計卻可能產生過多了類別,或是所有類別彼此相互聯繫,導致系統變得十分複雜,所以有人開始替一些常見的問題找尋一些良好設計範例,而這些範例是由彼此互動的物件所組成,稍加調整即可解決特定情境的一般性設計問題,(Erich Gamma & Richard Helm & Ralph Johnson & John Vlissides, 1995)也就是設計模式,設計模式這個名詞來至於建築學,由於物件導向概念也是來自於現實環境,所以便有人將設計模式引入物件導向中,設計模式(Erich Gamma etc., 1995),讓系統在設計上可以利用介面或是組合的方式去取代類別繼承來增加擴充性(J.M. Bieman & D. Jain & H.J. Yang, 2001)。在物件導向設計模式:可再利用物件導向軟體之要素中便提出了三大類,23種設計模式:
一、Creational模式系列:主要是用來處理物件生成的相關模式。物件導向的程式產生物件時分為兩個步驟:
(一)、利用類別所定義的屬性與方法去產生物件與記憶體空間。
(二)、呼叫類別的建構函數去初始化物件的內容。
而在GoF的Creational 模式為了上物件的產生能有較大的可擴充性,所以會使用其他功能類別或工廠物件建構物件本身的內容與功能,其建構的設計模式分別說明如下:
(一)、Abstract Factory 模式:定義一個物件工廠的介面,再利用實作該介面的工廠物件來對物件進行初始化。在圖2-3-1中的程式範例中,物件工廠的介面有兩個針對物件內容初始化的子介面,當需要變更物件的初始化內容時只需要變動實作功能初始化介面(ProductPart1,ProductPart2)的物件即可以變動物件的初始化內容。
圖2-3-1 Abstract Factory模式範例


(二)、Factory Method模式:這種方式則是先定義好物件生成步驟的介面與物件工廠,在依照不同類別分別實做每一個步驟的內容,最後由物件工廠來產生物件。在圖2-3-2中的程式範例中可以看到與Abstract Factory模式不同之處在於Factory Method模式的物件產生流程在產品介面中定義,而Abstract Factory模式則是在工廠介面中定義。
圖2-3-2 Factory Method模式範例


(三)、Builder模式:這種方式是將建構的流程先分為許多個步驟,然後建構函數再利用簡單的設定值去動態地產生所需的物件。在圖2-3-3 Builder模式範例中,Director物件依照config陣列中的設定,使用builder中的功能去初始化物件內容與功能。
圖2-3-3 Builder模式範例

(四)、Prototype模式:物件可以藉由複製的方式重新產生一個新的物件實體。
圖2-3-4 Prototype模式範例
(五)、Singleton模式:由時候由於系統資源有限或是要做統一個控制,會希望所產生的所有物件都有一個相同的實體,像是資料庫的連線或是檔案系統的物件在實作時都使用了這個模式。
圖2-3-5 Singleton模式範例


二、Structural 模式系列:針對不同的需求,提供一些物件間組合的模式。包含:
(一)、Adapter 模式:在系統開發時常使用一些其他的函式庫或是元件,所以需要將不同類別的功能結合再一起,而Adapter 模式便是透過物件複合技術或是多重繼承技術將不同類別的功能去產生一個新類別。
圖2-3-6 Adapter 模式概念圖
圖2-3-7 Adapter模式範例
(二)、Bridge模式:當要設計一些跨平台的系統時,常要面對不同平台間會有不同的系統呼叫與較底層的硬體操作,而不同的系統平台之間都有所差異,如果只是單純的利用判斷流程將所以所有平台的功能寫在每一個物件方法,就會讓物件便的龐大且不易維護,而Bridge模式使用類似與虛擬機的功能,在物件中先將功能以平台無關的介面方法設計,再另外根據不同的平台實作每一個虛擬的介面方法,這樣不僅功能邏輯被獨立出來,而且當要撰寫一個新平台時也不需要變動原本的程式與流程。
圖2-3-8 Bridge 模式概念圖
圖2-3-9 Bridge模式範例
(三)、Composite模式:系統設計上常要處理一些較複雜的資料結構,而Composite模式提供了一個常用的樹狀結構模型,讓程式可以用方法呼叫就能夠讀取樹狀結構中所有的物件,使用者可以任意增減與修改樹狀結構內容的物件而不需要而改變存取的方式。
圖2-3-10 Composite模式概念圖
圖2-3-11 Composite模式範例
(四)、Decorator模式: 一般物件導向程式設計都是使用繼承的方式在擴充物件的功能,而Decorator模式則是使用物件復合的技術來擴充物件的功能,它會先設計一組統一的呼叫介面,然後讓每一層物件去時做這些呼叫介面,再用類似俄羅斯套娃的結構方式去組合這些物件,這種模式的好處是物件間彼此沒有繼承關係,所以這些物件可以來至不同的功能模組,彼此之間也沒有太高的相依性,使用者甚至可以利用Builder的生成模式來動態的組合想要的功能模組。
圖2-3-12 Decorator模式概念圖
圖2-3-13 Decorator模式範例
(五)、Facade模式:在進行模組化的設計過程中,每一個功能模組都有可能需要使用其他功能模組的物件,但是一旦使用太多其他模組的物件,便會讓模組間的相依性太高,違背原本是用模組化設計來減少功能相依性的目的,所以Facade模式提供功能模組一個對外的總管窗口物件,這個總管窗口物件可以存取內部的所有物件,而其他功能模組只能透過這個總管物件進行存取。這樣不但讓功能模組內的物件設計變更時不會影響使用這個功能模組的相關程式,也可以讓在使用功能模組的程式有一個一致的介面。
圖2-3-14 Facade模式概念圖
圖2-3-15 Facade模式範例
(六)、Flyweight模式:程式設計上如果使用大量相同類型的物件,可能需要考慮系統資源消耗的問題,Flyweight模式透過讓每一個物件共用一些屬性與系統資源,但是用Singleton模式不同的是每一個物件可以保留自己的私有屬性,而Singleton模式則只會產生一個物件。
圖2-3-16 Flyweight模式概念圖
圖2-3-17 Flyweight模式範例
(七)、Proxy模式:這種模式提供一個代理與快取的物件,如果真正要執行功能的物件需要長時間的處理才能回應,或是處理縮花費的成本很高,可以Proxy模式配合回撥(Callback),利用一個代理物件提供之前產生的內容或是等到執行功能的物件回應資料後再呼叫回撥(Callback)函數將資料傳回給呼叫功能的程式。一般如果牽涉到硬體IO或是網路或WEB應用程式都需要使用Proxy模式來避免程式出現無回應的反應讓使用者產生困惑。
圖2-3-18 Proxy模式概念圖
圖2-3-19 Proxy模式範例
三、Behavioral模式系列:依照設計的需求,提供關於物件間互動行為的實作模式,這些模式的目的都是希望不要把物件間的行為「寫死」在程式中,而是希望能提供一個可動態擴充的功能讓物件間的互動行為能在執行時其動態的改變。包含:
(一)、Chain of Responsibility模式: Chain of Responsibility模式透過讓Facade或是Decorator等結構模式去產生物件,並且讓資料在物件內的所有子物件去進行判斷與處理。這種方式不只可以將判斷的條件可以動態擴充,判斷的邏輯條件也能整合到物件中。
圖2-3-20 Chain of Responsibility模式範例
(二)、Command模式:Command模式將物件的呼叫封裝成一個一個的指令,讓程式只要利用統一的指令呼叫介面便可以控制物件的行為。
圖2-3-21 Command模式範例
(三)、Iterator模式:Iterator模式讓程式可以用迴圈的程式邏輯去存取物件的內容。
圖2-3-22 Iterator模式範例
(四)、Strategy模式:這種模式是將相同需求的不同類別間的邏輯行為獨立成一組類別,而新的類別可以利用這一組邏輯行為類別組合成所需行為邏輯。
圖2-3-23 Strategy模式範例
(五)、Template Method模式:這種模式利用物件導向程式設計中的多型特性,將不同需求的程式邏輯在子類別中去實做。
圖2-3-24 Template Method模式範例
(六)、Observer模式:這個模式是實作一個訊息介面,物件在初始化時向訊息物件進行訂閱的動作,而訊息物件有所變動時便會通知所有訂閱的物件,這種方式可以減少物件間的相依性。
圖2-3-25 Observer模式範例
(七)、Mediator模式:這個模式是實作一個仲介物件去處理所有物件間的互動行為,任何物件都需要透過這個仲介物件來向其他的物件存取資料,與Observer模式不同的是物件是主動的向仲介物件要求資料,而在Observer模式中物件是被動地等待處訊息更新。
圖2-3-26 Mediator模式範例
(八)、State模式:這種模式是物件可以依照不同的狀態進行處理,讓物件在不同的狀態時相同的物件方法可以有不同的行為。這種模式可以讓物件動態的改變它的行為。
圖2-3-27 State模式範例
(九)、Memento模式:這個模式主要是將物件內部變化的狀態可以獨立出來進行儲存,讓物件有回朔的能力可以讓物件回復到之前的狀態,常用在實作系統交易行為的流程邏輯上。
圖2-3-28 Memento模式範例
(十)、Visitor模式:這個模式是讓回應物件方法的行為能根據不同的需求來源物件而有所不同,透過將回應物件本身作為參數進行傳遞到來源物件中,讓需求來源物件去進行實際的物件呼叫,而不是把判斷邏輯寫在回應物件的方法中,這種方式可以讓新增不同的需求來源類別不需要修改回應類別的方法。
圖2-3-29 Visitor模式範例
(十一)、Interpreter模式:Interpreter模式主要提供編譯程式分析程式語言去建立相關語意物件的模式。
圖2-3-30 Interpreter模式範例

圖2-3-31 Interpreter模式範例(續一)
圖2-3-32 Interpreter模式範例(續二)
總結來說,設計模式提供了物件導向的設計原則:
一、針對介面而寫,不要針對實作:介面指的是特定的物件操作定義的集合,它定義的相關操作的名稱,參數與傳回值,每一個物件可以實作許多介面,由於針對介面設計可以不需要知道對應物件的繼承情形,所以藉由介面來實作程式可以避免複雜的物件繼承問題並減少物件間的耦合性,也可以減少物件的類型,而當某些類別物件需要改寫時,由於對外的呼叫都是以介面,也不需要相關類別的繼承問題。
二、多用物件複合技術,少用類別繼承:類別是物件生成的模板,大部分物件導向的程式設計語言都是利用類別來產生物件,並且利用類別繼承去實作物件繼承的觀念,而物件複合則是指物件除了可以用繼承的方式獲得其他物件的行為與特性,還可以透過組合不同的物件去形成一個新的物件,由於大部分的物件導向的程式語言都只支援單一的類別繼承,不允需子類別有兩個以上的父類別,所以使用物件複合技術可以避免類別繼承所造成的限制。
在程式的實作階段如果只是考慮完成目前功能方面的需求,則可能會導致程式碼越來越複雜,也越來越難以維護,所以必須要考慮未來需求擴充與變更的可能行,並應用良好的設計模式到實作中,避免未來可能的需要與減少維護的難度,此外設計模式也可以解決一些常見的結構或邏輯上面的需求。所以本研究在系統實作上會使用設計模式進行處理可能需要擴充的部分並減少以後維護的難度。


沒有留言: