大話設計模式中的總結

背景介紹

嘉賓:

面向對象思想
抽象
封裝
繼承
多態

評委:

單一職責
開放封閉
依賴倒轉
里氏替換
合成複用
迪米特

類型:

創建型模式
結構性模式
行爲型模式

創建型比較

標題爲什麼需要創建型模式?
抽象工廠:

答:創建型模式隱藏了這些類的實例是如何被創建和放在一起,整個系統關於這些對象所知道的是由抽象類所定義的接口。這樣,創建型模式在創建了什麼、誰創建它、它是怎麼被創建的,以及何時創建這些方面提供了很大的靈活性[DP1。

原型模式:

答:當一個系統應該獨立於它的產品創建、構成和表示時,應該考慮用創建性模式。建立相應數目的原型並克隆它們通常比每次用合適的狀態手工實例化該類更方便一些[DP]。”

依賴倒轉問請談談你對松耦合的理解?

標題建造者模式:

答:這個問題首先要談談內聚性與耦合性,內聚性描述的是一個例程內部組成部分之間相互聯繫的緊密程度。而耦合性描述的是一個例程與其他例程之間聯繫的緊密程度。軟件開發的目標應該是創建這樣的例程:內部完整,也就是高內聚,而與其他例程之間的聯繫則是小巧、直接、可見、靈活的,這就是松耦合(DPE]。”

標題建造者模式是如何實現松耦合?

答:將一個複雜對象的構建與它的表示分離,這就可以很容易地改變一個產品的內部表示,並且使得構造代碼和表示代碼分開。這樣對於客戶來說,它無需關心產品的創建過程,而只要告訴我需要什麼,我就能用同樣的構建過程創建不同的產品給客戶[DP]。”

單例模式與其它模式的不同?

對一些類來說,一個實例是很重要的。一個全局變量可以使得一個對象被訪問,但它不能防止客戶實例化多個對象。我的優勢就是讓類自身負責保存它的唯一實例。 這個類可以保證沒有其他實例可以被創建,並且我還提供了一個訪問該實例的方法。這樣就使得對唯一的實例可以嚴格地控制客戶怎樣以及何時訪問它[DP]。”

工廠模式方法對創建型模式存在的意義?

創建型模式抽象了實例化的過程。它們幫助一個系統獨立於如何創建、組合和表示它的那些對象。創建型模式都會將關於該系統使用哪些具體的類的信息封裝起來。允許客戶用結構和功能差別很大的‘產品’ 對象配置一個系統。配置可以是靜態的,即在編譯時指定,也可以是動態的,就是運行時再指定。[DP]”
“我覺得她們幾位都可能設計出比我更加靈活的代碼,但她們的實現也相對就更加複雜。通常設計應該是從我,也就是工廠方法開始,當設計者發現需要更大的靈活性時,設計便會向其他創建型模式演化。當設計者在設計標準之間進行權衡的時候,瞭解多個創建型模式可以給設計者更多的選擇餘地。(DP]”

勝出:工廠方法

---------------------------------------------------------------------------------------

結構性模式比較

適配器模式:

面向對象的精神就是更好地應對需求的變化,而現實中往往會有下面這些情況,想使用一個已經存在的類,而它的接口不符合要求,或者希望創建一個可以複用的類,該類可以與其他不相關的類或不可預見的類協同工作。正如開放封閉所倡導地對修改關閉,對擴展開放的原則,我可以做到讓這些接口不同的類通過適配後,協同工作。[DP]”

面對變化 橋接怎麼做?

繼承是好的東西,但往往會過度地使用,繼承會導致類的結構過於複雜,關係太多,難以維護,而更糟糕的是擴展性非常差。而仔細研究如果能發現繼承體系中,有兩個甚至多個方向的變化,那麼就解耦這些不同方向的變化,通過對象組合的方式,把兩個角色之間的繼承關係改爲了組合的關係,從而使這兩者可以應對各自獨立的變化,事實上也就是合成聚合複用所提倡的原則,總之,面對變化,我主張‘找出變化並封裝之’。[DPE1”
裝飾怎麼做?
面對變化,如果採用生成子類的方法進行擴充,爲支持每一種擴展的組合,會產生大量的子類,使得子類數目呈爆炸性增長。這也是剛纔橋接小姐所提到的繼承所帶來的災難,而事實上,這些子類多半隻是爲某個對象增加一些職責,此時通過裝飾的方式,可以更加靈活、以動態、透明的方式給單個對象添加職責,並在不需要時,撤銷相應的職責。[DP]”

組合模式如何做到表示對象的部分與整體的層次結構?

我是希望用戶忽略組合對象與單個對象的不同,用戶將可以統一地使用組合結構中的所有對象。“用戶使用組合類接口與組合結構中的對象進行交互,如果接收者是一個葉節點,則直接處理請求,如果接收者是組合對象,通常將請求發送給它的子部件,並在轉發請求之前或之後可能執行一些輔助操作。組合模式的效果是客戶可以-致地使用組合結構和單個對象。任何用到基本對象的地方都可以使用組合對象。[DP]”

外觀模式對信息的隱藏促進了軟件的複用[J&DP]的理解:

類之間的耦合越弱,越有利於複用,一個處在弱耦合的類被修改,不會對有關係的類造成波及。如果兩個類不必彼此直接通信,那麼就不要讓這兩個類發生直接的相互作用。如果實在需要調用,可以通過第三者來轉發調用。[J&DP]"

外觀模式是如何貫徹這一原則?

讓一個軟件中的子系統間的通信和相互依賴關係達到最小,而具體辦法就是引入一個外觀對象,它爲子系統間提供了一個單一而簡單的屏障[DP]。通常企業軟件的三層或N層架構,層與層之間地分離其實就是外觀模式的體現。”外觀小姐說話很慢,但顯然準備過,並沒說錯什麼。迪米特滿意

享元模式如何看待很多對象使得內存開銷過大:

“對象使得內存佔用過多,而且如果都是大量重複的對象,那就是資源的極大浪費[DP],會使得機器性能減慢,這個顯然是不行的。”享元說,“面向對象技術有時會因簡單化的設計而代價極大。比如文檔
處理軟件,當中的字符都可以是對象,而如果讓文檔中的每一個字 符都是一個字符對象的話,這就會產生難以接受的運行開銷,顯然這是不合理也是沒必要的。由於文檔字符就是那麼些字母、數字或符號,
完全可以讓所有相同的字符都共享同一個對象,比如所有用到‘a’ 的字符的地方都使用一一個共享的 ‘a’對象,這就可以節約大量的內存。”

代理模式,請對比一下你和外觀模式有哪些不同?與適配器模式又區別在何處?”

代理與外觀的主要區別在於,代理對象代表一個單一對象而外觀對象代表一個子系統:代理的客戶對象無法直接訪問目標對象,由代理提供對單獨的目標對象的訪問控制,而外觀的客戶對象可以直接訪問子系統中的各個對象,但通常由外觀對象提供對子系統各元件功能的簡化的共同層次的調用接口。[R2P]" “至於我與適配器,其實都是屬於一種銜接 性質的功能。代理是一種原來對象的代表,其他需要與這個對象打交道的操作都是和這個代表交涉。而適配器則不需要虛構出一個代表者,只需要爲應付特定使用目的,將原來的類進行一些組合。 [DP]”

橋接適配器外觀三種模式,再次PK

適配器:

適配器說:“我主要是爲了解決兩個已有接口之間不匹配的問題,我不需要考慮這些接口是怎樣實現的,也不考慮它們各自可能會如何演化。我的這種方式不需要對兩個獨立設計的類中任一個進行重新設計,就能夠使它們協同工作。[DP]”

橋接:

“我覺得我和適配器小姐具有一些共同的特徵,就是給另一對象提供一定程度的間接性,這樣可以有利於系統的靈活性。但正所謂未雨綢繆,我們不能等到問題發生了,再去考慮解決問題,而是更應該在設計之初就想好應該如何做來避免問題的發生,我通常是在設計之初,就對抽象接口與它的實現部分進行橋接,讓抽象與實現兩者可以獨立演化。顯然,我的優勢更明顯。[DP]”

外觀:

橋接和適配器是被用於軟件生命週期的不同階段,針對的是不同的問題,談不上孰優孰劣。然後,對於我來說,和適配器還有些近似,都是對現存系統的封裝,有人說我其實就是另外一組對象的適配器,這種說法是不準確的,因爲外觀定義的是一個新的接口,而適配器則是複用一個原有的接口,適配器是使兩個已有的接口協同工作,而外觀則是爲現存系統提供一個更爲方便的訪問接口。如果硬要說我是適配,那麼適配器是用來適配對象的,而我則是用來適配整個子系統的。也就是說,我所針對的對象的粒度更大。[DP]”

勝出:外觀模式

---------------------------------------------------------------------------------------

行爲型模式比較(一)

模板方法,你對代碼重複的理解以及你如何實現代碼重用?

“代碼重複是編程中最常見、最糟糕的‘壞昧道’, 如果我們在一個以上的地方看到相同的程序結構,那麼可以肯定,設法將它們合而爲一,程序會變得更好[RIDEC]。但是完全相同的代碼當然存在明顯的重複,而微妙的重複會出現在表面不同但是本質相同的結構或處理步驟中[R2P],這使得我們一定要小心處理。繼承的一個非常大的好處就是你能免費地從基類獲取一些東西,當你繼承一個類時,派生類馬上就可以獲得基類中所有的功能,你還可以在它的基礎上任意增加新的功能。模板方法模式由一個抽象類組成,這個抽象類定義了需要覆蓋的可能有不同實現的模板方法,每個從這個抽象類派生的具體類將爲此模板實現新方法[DPE]。這樣就使得,所有可重複的代碼都提煉到抽象類中了,這就實現了代碼的重用。”

下面請問命令模式,爲什麼要將請求發送者與具體實現者分離?這有什麼好處?”單一職責問道。

“您的意思其實就是將調用操作的對象與知道如何實現該操作的對象解耦,而這就意味着我可以在這兩者之間處理很多事,比如完全可以發送者發送完請求就完事了,具體怎麼做是我的事,我可以在不同的時刻指定、排列和執行請求。再比如我可以在實施操作前將狀態存儲起來,以便支持取消/重做的操作。我還可以記錄整個操作的日誌,以便以後可以在系統出問題時查找原因或恢復重做。當然,這也就意味着我可以支持事務,要麼所有的命令全部執行成功,要麼恢復到什麼也沒執行的狀態。總之,如果有類似的需求時,利用命令模式分離請求者與實現者,是最明智的選擇。”

職責鏈模式,回答同樣的問題:

“我們時常會碰到這種情況,就是有多個對象可以處理一個請求, 哪個對象處理該請求事先並不知道,要在運行時刻自動確定,此時,最好的辦法就是讓請求發送者與具體處理者分離,讓客戶在不明確指定接收者的情況下,提交一個請求,然後由所有能處理這請求的對象連成一條鏈, 並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。

狀態模式,條件分支的大量應用有何問題?如何正確看待它?

狀態答道:“如果條件分支語句沒有涉及重要的商務邏輯或者不會隨着時間的變化而變化,也不會有任何的可擴展性,換句話說,它幾乎不會變化,此時條件分支是應該使用的。但是注意我這裏用到了很多前提,這些前提往往都是不成立的,事實上不會變化的需求很少,不需要擴展的軟件也很少,那麼如果把這樣的分支語句進行分解並封裝成多個子類,利用多態來提高其可維護、可擴展的需要,是非常重要的。狀態模式提供了一個更好的辦法來組織與特定狀態相關的代碼,決定狀態轉移的邏輯不在單塊的if或switch中,而是分佈在各個狀態子類之間,由於所有與狀態相關的代碼都存在於某個狀態子類中,所以通過定義新的子類可以很容易地增加新的狀態和轉換。[DP]”

勝出:觀察者模式

---------------------------------------------------------------------------------------

行爲型模式比較(二)

“請問解釋器模式,說說你參賽的動機和優勢?”依賴倒轉問道。

解釋器小姐很鎮定地答道:“在編程世界裏,實現目標都是通過編寫語言並執行來實現的,從最低級的機器語言到人能很容易讀懂機器也可以執行的高級語言,但是高級語言編寫起一些問題 可能還是比較複雜。如果一種特定類型的問題發生的頻率足夠高,那麼就可以考慮將該問題的各個實例表述爲一個簡單語言中的句子。也就是說,通過構建一個解釋器,該解釋器解釋這些句子來解決該問題[DP]。比
如正則表達式就是描述字符串模式的一種標準語言, 與其爲每一個字 符串模式都構造一個特定的算法,不如使用一種通用的搜索算法來解釋執行一個正則表達式,該正則表達式定義了待匹配字符器的集合[DP]。”

“中介者模式,人家都說你是交際花,請問你廣交朋友的目的是什麼?”迪米特問道。

“交際花不敢當,但我的確喜歡交朋友。面向對象設計鼓勵將行爲分佈到各個對象中,這種分佈可能會導致對象間有許多連接。也就是說,有可能每一個對象都需要知道其他許多對象。對象間的大量相互連接使得一個對象似乎不太可能在沒有其他對象的支持下工作,這對於應對變化是不利的,任何較大的改動都很困難[DP]。所以說朋友多既是好事情,其實也是壞事情。我提倡將集體行爲封裝一個單獨的中介者對象來避免這個問題,中介者負責控制和協調一組對象間的交互。中介者充當一箇中介以使組中的對象不再相互顯式引用。這些對象僅知道中介者,從而減少了相互連接的數目[DP]。我作爲中介者,廣交朋友,就起到了在朋友間牽線搭橋的作用。可以爲各位朋友們服務。這其實不也正是迪米特先生您一直倡導的最少知識原則,也就是如何減少耦合的問題,類之間的耦合越弱,越有利於複用[J&DP]。"

合成聚合複用問道: “訪問者模式,聽說你對朋友要求很苛刻,要請到你幫忙是很難的事情,你喜歡交朋友嗎?”

聽到這個問題,訪問者笑開了顏:“哪有這種事情,朋友要我幫忙,我都會盡力而爲的。的確,我不太喜歡交很多朋友,一般找到好朋友了,就不喜歡再交往新的朋友了。我的理念是朋友在精不在多。但是我和朋友間的交往通常會是多方面的,一同聊天、逛街、旅遊、唱歌、游泳,哪怕是我們不會的活動,我們也可以嘗試起去學習、去擴展我們的生活情趣。也就是說,訪問者增加具體的Element是困難的,但增加依賴於複雜對象結構的構件的操作就變得容易。僅需增加一個新的訪問者即可在一個對象結構.上定義一個新的操作。”

“請問策略模式,說說你對‘優先使用對象組合,而非類繼承’的理解?”合成聚合複用問道。

策略小姐答得很流利,“繼承提供了一種支持多種算法或行爲的方法,我們可以直接生成一個類A的子類B、C、D,從而給它以不同的行爲。但這樣會將行爲硬行編制到父類A當中,而將算法的實現與類A的實現混合起來,從而使得類A難以理解、難以維護和難以擴展,而且還不能動態地改變算法。仔細分析會發現,它們之間的唯一差別是它們所使用的算法或行爲,將算法封裝在獨立的策略Strategy類中使得你可以獨立於其類A改變它,使它易於切換、易於理解、 易於擴展[DP]。這裏顯然使用對象組合要優於類繼承。”

“請問一下備忘錄模式,在保存對象的內部狀態時,爲何需要考慮不破壞封裝細節的前提?”單一職責問道。

“通常原對象A都有很多狀態屬性,保存對象的內部狀態,其實也就是將這些狀態屬性的值可以記錄到A對象外部的另一個對象B,但是,如果記錄的過程是對外透明的,那就意味着保存過程耦合了對象狀態細節。使用備忘錄就不會出現這個問題,它可以避免暴露-些 只應由對象A管理卻又必須存儲在對象A之外的信息。備忘錄模式把可能很複雜的對象A的內部信息對其他對象屏蔽起來,從而保持了封裝邊界[DP]。

“迭代器模式,說說迭代器模式對遍歷對象的意義?”里氏代換問道。

“一個集合對象,它當中具體是些什麼對象元素我並不知道,但不管如何,應該提供一種方法來讓別人可以訪問它的元素,而且可能要以不同的方式遍歷這個集合。迭代器模式的關鍵思想是將對列表的訪問和遍歷從列表對象中分離出來並放入一個迭代器對象中,迭代器類定義了一個訪問該列表元素的接口。迭代器對象負責跟蹤當前的元素,並且知道哪些元素已經遍歷過了[DP]。”

勝出:策略模式

插圖(一)設計模式PK

設計模式PK

插圖(二)設計模式的UML圖

在這裏插入圖片描述

uml類圖連接:https://download.csdn.net/download/lsyrhz/12526138。

以上爲本人學習《大話設計模式》一書的簡單總結,隨着以後對設計模式理解的加深,會記錄更優質的學習筆記。也歡迎各位大佬提出指點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章