在前兩篇部落格中詳細的介紹了"政策模式"和“觀察者模式”,今天我們就通過花瓶與鮮花的例子來類比一下“裝飾模式”(Decorator Pattern)。在“裝飾模式”中很好的提現了開放關閉原則,即類應該對擴充開放對修改關閉。裝飾者模式可以讓我們在不對原來代碼的修改的情況下對類進行擴充。這也好比我們往花瓶裡插花,我們在插花的時候是不會對花瓶以及原來的話進行任何的修改,而隻管将我們新的花添加進花瓶即可。這就是我們的裝飾者模式。當然本篇部落格中所采用的語言仍然是Swift語言。
裝飾者模式,用另一種表達方式就是“對原有的物體進行裝飾,給原有的物體添加上新的裝飾品”。舉個栗子,比如一個禮物,我們要對其進行包裝,禮物是被裝飾者(我們稱為元件---Component),而包裝盒以及包裝盒上的花等等就是裝飾品(我們成為裝飾者---Decorator)。如果換成花瓶與鮮花的關系,花瓶就是Component,而鮮花就是Decorator。下方引用了裝飾者模式的定義:
裝飾者模式:動态地将責任附加到對象上。若要擴充功能,裝飾着提供了比繼承更有彈性的替代方案。
一、使用“類圖”分析鮮花+花瓶的裝飾關系
與之前部落格的風格類似,我們還是依托于執行個體來了解“裝飾者模式”,我們就依托于花瓶與鮮花的關系來了解一下裝飾者模式。在之前的部落格中我們提到過一條設計原則“封裝變化”,也就是說要将變化的東西進行封裝提取。在“裝飾者模式”中所使用的裝飾就是變化的部分,也就是Decorator是變化的部分對應着我們的鮮花,因為往花瓶中插花的過程就是鮮花變化的過程,也就是為花瓶裝飾的過程。而花瓶就是元件了。
在“裝飾者模式”中需要注意的是,這裡所謂的裝飾者不單單就是我元件添加的新的裝飾品。一個裝飾者對象就是添加該裝飾後的元件,也就是說裝飾者=舊元件 + 新裝飾品,了解這一點是非常重要的。具體請看下方的元件與裝飾者之間的關系:

下方的類圖就是我們将要實作的“裝飾者模式”的執行個體,也就是鮮花和花瓶的關系。下方所有類的基類是VaseComponent(花瓶元件),在VaseComponent類中的description字段是用來描述某某花瓶中裝有某某花的,display()方法用來列印description描述資訊的。上方的紅框就是所有的鮮花(裝飾者---Decorator),所有鮮花裝飾者的基類是FlowerDecorator,當然FlowerDecorator也是繼承自VaseComponent(花瓶元件)的,因為裝飾者擁有被裝飾的對象同時又有新添加的裝飾物(見上圖)。在這些裝飾者類中包含一個字段,該字段就是VaseComponent的對象(花瓶元件的對象)。該對象可以指沒有任何裝飾的花瓶,也可以指已經添加了裝飾的花瓶。無論是裝飾者還是被裝飾者都有共同的基類,是以我們就可以利用多态來實作“裝飾者模式”。
在上面類圖的FlowerDecorator中的VaseComponent對象就對應着本部分第一張圖中裝飾者中包括的那個元件。該元件是最近一次裝飾過的元件,而裝飾者所負責的事情就是在該元件上添加上該裝飾者特有的裝飾品。換句話說,此時的裝飾者的對象就是最新的元件對象。也許經過這些原理的講解,你會有些迷惑,那麼不要着急,在具體代碼實作時會帶你撥開雲霧見日出的。
二、“花瓶+鮮花”具體代碼實作(Swift版)
當然,我們此處的代碼實作與上面的“類圖”的設計是一緻的。看完代碼再結合着上面的“類圖”你會對裝飾者模式有更好的了解。下方我們會一步步的給出代碼具體實作,當然下方的類名,成員變量以及成員方法的命名與上述類圖一直。
1.實作空花瓶的基類(VaseComponent)
花瓶的基類VaseComponent就是我們被修飾者也就是我們所有花瓶(元件)的基類了。當然VaseComponent不僅僅是所有花瓶的基類,它還是所有裝飾者的基類,因為裝飾者對象 = 舊元件 + 新裝飾品 = 新元件。在該類中的description字段中存儲的是花瓶的描述資訊,比如“瓷花瓶”,“玻璃花瓶”等資訊。getDescription()->String方法是用來擷取description存儲的描述資訊的。display()->Void方法就是對getDescription()方法擷取到的值進行列印, 具體實作如下所示。
2.建立我們的空花瓶
在第一步中我們建立了空花瓶的基類,緊接着我們要實作具體的花瓶。在下方代碼中我們建立了兩個空花瓶,一個是Porcelain瓷花瓶,一個是Glass玻璃花瓶。并且在調用父類初始化器時為父類中的description字段進行初始化。空花瓶比較簡單,代碼也不多,空花瓶就是一個坯子,等着其他鮮花來做修飾,具體實作如下所示。
3. 鮮花基類(FlowerDecorator)的實作
FlowerDecorator也就是所有裝飾者的基類,這裡與其稱為鮮花基類,還不如成為所有“新花瓶”的基類。什麼是“新花瓶”呢?我們暫且成為添加了新的鮮花種類的花瓶為“新花瓶”。FlowerDecorator是所有鮮花裝飾者的基類,而FlowerDecorator繼承自VaseComponent類。在FlowerDecorator中添加了一個vase字段,該字段是VaseComponent類型,用于存儲“舊元件”,也就是上一次被修飾過的花瓶元件。這也是所有裝飾者都包含的字段,“裝飾者”在初始化時會指定上次被修飾後元件(空花瓶或者其他修飾者的對象)。也就是說vase字段中存儲的可以是一個空的花瓶對象,也可以是其他“裝飾者”類的對象。具體實作如下所示:
4.實作各個裝飾者(Decorator)
上方我們已經建立好了裝飾者的基類,在裝飾者基類中含有最新的元件(花瓶的狀态,還有多少種類的花)。而在“裝飾者”的類中我們要将具體花的品種,也就是我們變化的部分添加進具體的裝飾者的實作中。下方的第一個類是我們的玫瑰花Rose類,重寫了基類的getDescription()方法,在該方法中,為上一個裝飾者添加了新的裝飾品,也就是“玫瑰”。而在百合花Lily的類中我們為元件添加了“百合”裝飾品。具體實作方式如下所示:
三、“萬事俱備,隻欠東風”--建立測試用例
經過上面的兩大步,我們的裝飾者模式的代碼實作也就做完了。但是上面隻是實作,沒有測試用例的驅動,上面的示例看上去不夠直覺。為了搞清楚其工作方式,我們的測試用例還是必不可少的。下方就是我們的測試用例的代碼:
在上述代碼中呢,我們首先建立了一個空的瓷花瓶的對象procelain,緊接着列印描述資訊(輸出“瓷花瓶”)。然後為該瓷花瓶的對象procelain添加上Rose和Lily裝飾。當然我們仍然使用procelain變量來接收添加Rose修飾後的對象(也就是Rose類的對象),此時Rose類的對象代表着“插有玫瑰花的瓷花瓶”。緊接着,我們在Rose對象的基礎上添加了Lily裝飾,添加Lily裝飾後,porcelain就是Lily類的對象,表示“插有玫瑰花,百合花的瓷花瓶”。最後調用display()方法列印最新的描述資訊。
在上述測試用例中,我們為porcelain對象添加了兩個裝飾品,最終的porcelain對象是Lily的對象,它是空瓷瓶+玫瑰+百合花的組合體。當最終該測試用例的Lily對象porcelain調用display()方法時,在display()方法中會調用該對象中的getDescription()方法,而該對象中的getDescription()方法會調用上一個修飾者(此處是Rose)對象的getDescription()方法,最終會找到我們的元件,也就是我們的空瓶子(Porcelain)中的的getDescription()方法。具體調用方式如下圖所示:
今天關于“裝飾者模式”的完整執行個體就先到這。
同樣,在本篇部落格的末尾,我們給出類本篇部落格是Dmeo, Github: https://github.com/lizelu/DesignPatterns-Swift
作者:青玉伏案
出處:http://www.cnblogs.com/ludashi/
本文版權歸作者和共部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。
如果文中有什麼錯誤,歡迎指出。以免更多的人被誤導。
收履歷:某網際網路公司,招聘iOS/Android靠譜工程師,入職後,可内部聯系樓主,有小禮品贈送,有意者可郵箱投遞履歷:[email protected]