← 返回卷宗
編程筆記

簡單說說對編程中設計模式的理解

設計模式
設計模式主要是針對面向對象的思考方法,即將萬物都抽象成類來描述,然後基於類生成具體的對象。 這個類其實就是一個模板,所以最基礎的設計模式便是模板模式,模板模式非常簡單,即定義一個基類,然後給出來一些虛擬或抽象方法,具體的實現在子類中完成,這是面向對象編程裡最基礎也最簡單的用法。 如一篇文章,它有標題,有內容,那麼這個就可以成為一個模板,標題作為一個方法,內容作為一個方法,這樣子類中返回的字符不同,便能夠成為不同的類型文章,如在標題字符串描述裡,可以是有有圖像與無圖像的。 模板方法強調在對基類的調用上擁有統一的接口,這樣才能夠實現足夠的延展性,它強調的是模板式。 而更進一步,如果僅僅接口方法只是很少量時,可以視作為策略模式,比如定義一個方法名稱,是兩個整型參數,在方法內部可以實現加法,也可以實現減法,也可以實現乘法等各種處理,在外部看來選擇不同的繼承類,就能夠實現不同的功能,所以這就成為了策略模式。 策略模式與模板模式的最大區別在於,策略模式關注的是實現選擇,而這個在策略模式中,通常是引入一個Context 的上下文類,策略本身的基類是策略模板,而選擇什麼策略,則由Context 來負責調用。 為什麼要有一個Context,比如出現多個策略需要連續判斷時,Context調用一個策略類的方法後,還可以記錄下該策略的結果,然後帶著上個的結果傳入到下一個策略方法中,然後再進行計算。 換句話說,引入Context上下文類創建的對象,目的就是為了記錄並傳遞策略的結果,所以它可以是上下文相關的。 一種常見場景就是用Context記錄當前運算狀態或步驟,比如處理一張圖片,先進行了灰度處理,然後再進行提輪廓,因為有了上下文的對象,系統隨時可以知曉當前的狀況,無論對於異步處理還是多線程編程都是非常有益的。 對於能直接新建的對象,只需要直接創建就好了,但是總是事不如人意的,有時候對於產生的對象是需要統一管理的,尤其是複雜的對象,同時並不需要上下文有那麼密切的相關性。 比如說生產一輛轎車後,再生產一輛卡車,這是基於不同需求的選擇,但是生產轎車與生產卡車之間並無上下文聯繫的需求,它們甚至是可以進行同步進行的。 這種情況下,無疑可以考慮使用工廠模式,在同一個工廠出廠的對象無疑都可以打上相應標誌,並且事後無論是要統一銷燬還是怎麼處理,都會非常方便。 工廠模式中只關心產生對象,所提供的方法接口,對於產生的順序或是之間需要什麼組合要求,並不關心。 簡單的工廠對於一些簡單場景是很有用的,比如說,一個日誌記錄,是記錄到硬盤還是記錄到內存還是記錄在哪裡,可以用工廠產生記錄對象來後期決定。 但是打開工廠方法,對於一輛車的內部流程,涉及到了具體的組裝時,就需要採用不同的方法進行構建。 這種構建的流程大多是可以複用的,比如給封閉轎車安輪子與給敞篷型轎車安輪子,並沒有不同的地方。 但是一輛車安車頂與安輪子顯然是兩個不同的活,這個無法直接通過策略模式完成,因為策略模型可以關心完成安什麼樣的輪子,安輪子的步驟,卻在概念上不能組裝車子。 所以在這裡又有了構建者模式,構建者模式中,複雜的構建與其表示相分離,它類似於策略模式,但是來源的構造並不基於同一個策略類。 一系列策略可以構成同一類零件,而不同零件可以構成不同的部件,而不同部件則可以構建出一個成品。 這就是它們的區別,在構建者模式中,包含了零件與部件,比如說一輛車可以安個圓頂,也可以安個方頂,那麼這可以用構建者模式來實現圓頂構建者或方頂構建者。 構建者模式的最大特點是:它是可以在現有對象上,將不同部件組成一個新對象。 如一個漢堡,用紙裝或用盒子裝,而裝的漢堡可能用奧爾良雞腿,也可能是用油炸雞腿所構成,而到底是用紙或盒還是用什麼雞腿,這之間並無任何關係,這就比較適合用構建者模式。 對象之間的關係並不是完美而簡單的,有時候會出現相互對象的依賴,這會非常麻煩,最有效的方法是除了統一調度外,還必須考慮有意外的情況,從而能夠進行臨時調度。 比如高鐵的各個列車,它們的先後次序很重要,需要並行不紊的進行,如果依賴於高鐵列車之間相互通訊必然會引起混亂,因為消息的傳播效率很低。 所以這就需要一個廣播機制,一箇中介的對象來與其它對象之間進行交互,這就好比一個數據中心,而其它對象只是各個終端,所有的終端都只與數據中心進行通訊。 這種情況所設立的中心,被稱為中介者,將數據廣泛發給所有的其它相應對象可以看到,這就類似於一個頻道,然後所有的對象信息都在這上面進行交互。 這種星型結構,也是現代網絡最常見的結構,就目前來說它是一個相對較好的方案。 對於終端的接入,有時候需要判斷的它的狀態,例如是否已經接入,或是已經斷開,以確定數據可以發送,這種情況稱為事件。 它並不侷限於這個場景使用,但以這個場景為例,這種基於事件的模型已經在一些語言裡被實現得很好,即事件模型,通過調用某一個事件來實現狀態通知。 因為事件是離散的,所以必須要用離散的方法進行處理,這可以使用輪循或是方法調用來實現,事件的訂閱與取消,這就是發佈者訂閱者模式,同樣也可以是稱為拜訪者模式。 這種模式的好處是,對於接入的終端可以開放權限,透露一些對象內部的細節,比如一個類中的成員變量值是非公開的,但數據中心可以通過事件方法將這個值告訴接入者,一方面保持了外面接口的統一,另一方面可以針對特殊需要,給外來接入的對象公開一些私有的值。 比如一個窗口上的按鈕,它的點擊事件發生時,要傳出內部的某個值,這個是被允許的,同樣甚至可以允許傳出一個核心的對象讓外部進行改變,這顯然是違反封裝性原則的,不過允許哪些可以對外傳出,實際上還是由類的設計來控制的。 如果A事件引起B事件,而B事件滿足某條件引起C事件,這種就可以稱為責任鏈模式,一種典型場景比如選擇省市地區街道時,它會進行一層層的過濾,事實上便構成了一個事件鏈。 責任鏈模式顯而易現的是,某個對象的狀態改變基於某一個對象改變的通知,這樣會帶來很多好處。 比如說當一個省被選擇後,那麼省被選擇定了,就要篩選出在B中顯示哪些列表,但假如A是一個類,B是一個類,A如果去直接控制B的類的成員,會引起很麻煩,設計起來考慮東西很多,因為A究竟應該能夠直接控制多少B的成員才能滿足設計需要,在複雜場景下很難說。 所以不如只由A通知B,這裡選中了某個值,然後交給B,B根據這個值自己決定應該呈現什麼數據,這樣靈活性與擴展性就大大增強了,同時關於B的內部成員的職責與作用也被控制在了B之內。 如果是在多人協作編程項目時,A類的編寫者只需要編寫A類,B類只需要負責自己的B類就可以了,這樣職責會非常明確,管理起來會非常方便。 對象在傳遞的時候,有時候原本的對象是不夠中的,需要附加上一些信息,在一些動態語言裡這個事情比較簡單,直接擴展屬性就行了,但是這樣有時候會引起一些紛爭,因為原始對象被修改了,並且這種修改通常不可逆,新增的動態屬性並不能刪除掉。 所以乾脆不如另外做一個新類,將它進行包裝,而這個原來的對象成為新類的成員之一,這樣就構成了裝飾器模式。 裝飾器模式有個最大的好處時,可以隨時丟掉新創建的對象,然後仍然使用原來的對象,所以顯得非常靈活。  

本文由 三符道長 撰於 2018年4月1日。轉載請註明出處。