![](http://ww4.sinaimg.cn/large/006tNc79ly1g4ztauvhzej30p00dw45p.jpg) ## 前言 當應用開發中,我們要為一個對象在原有功能上進行擴展增強時,往往採用繼承的方式,而繼承過多時就會使得功能類更加複雜,不利於維護,而設計模式中裝飾者模式 ...
![](http://ww4.sinaimg.cn/large/006tNc79ly1g4ztauvhzej30p00dw45p.jpg) ## 前言 當應用開發中,我們要為一個對象在原有功能上進行擴展增強時,往往採用繼承的方式,而繼承過多時就會使得功能類更加複雜,不利於維護,而設計模式中裝飾者模式可以幫助我們更好對應這種場景,裝飾者模式可以做到讓對象能夠動態地進行功能擴展,而不影響其他對象. 那究竟它是如何實現的呢,又如何實際應用呢,就讓我們一起來學習下這個模式吧。 ## 模式定義 裝飾者模式屬於結構型設計模式,首先我們先瞭解下裝飾者模式的定義。 > In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern. 簡而言之, 裝飾者模式就是動態地為一個對象擴展額外的功能,無論是動態還是靜態,都不影響相同類的其他對象的行為, 這樣使得對象在運行時更加靈活。 裝飾者實現的方式通常為組合或者繼承,可以讓客戶端根據需求進行對應的裝飾,來達到功能增加的目標。為了簡化理解,裝飾者模式中裝飾一詞其實就是給原來的對象添加額外功能。 ## 模式結構 接下來我們看下裝飾者模式的層次結構和主要角色. ![image-20181105081809413](https://ws4.sinaimg.cn/large/006tNbRwly1fwwvgxjzzbj30fs0aht9o.jpg) * `Component` 抽象組件,最原始,核心的對象,通常為介面或者抽象類. * `ConcreteComponent` 具體組件,對 `Component` 的實現,也是需要裝飾的對象. * `Decorator` 裝飾者, 通常為抽象組件的抽象實現, 它的屬性一定有私有變數指向 `Component`. * `ConcreteDecorator` 具體裝飾對象,是 `Decorator` 的具體實現, 用於將原始,核心的方法裝飾增強的類. 從圖中看到,介面 `Component` 會有對應的實現類 `ConcerateComponent`,要對具體實現類進行功能增強,就需要對應的具體裝飾者 `ConcreteDecorator` ,它通過內部引用 `Component` 類型的 `ConcerateComponent` 對象 ,在介面方法的預設實現上,允許添加額外的邏輯和功能代碼。 ## 模式實現 現在我們用喝咖啡的例子來實現下裝飾者模式,假設我們要買一杯咖啡,需要加糖,加奶,而什麼都不加的咖啡與加奶,加糖的價格都不一樣,我們就要計算調製一杯加糖加奶的咖啡需要花費多少錢。 這裡咖啡就是我們的具體組件 `ConcerateComponent`, 奶和糖就是具體裝飾對象 `ConcreteDecorator`。 為了簡單,我們先創建一個`Component`介面類 `Drink`, 定義兩個方法用來獲得價格和描述: ![](http://ww2.sinaimg.cn/large/006tNc79ly1g4ztsfc1a7j311s082q3f.jpg) 接著,創建一個 `ConcreteComponent` 類 `Coffee` 實現 `Drink` 介面. ![](http://ww2.sinaimg.cn/large/006tNc79ly1g4ztspsoi1j311s0j60ua.jpg) 現在就需要一個抽象類 `DrinkDecorator`,用於擴展實現功能增強. ![](http://ww4.sinaimg.cn/large/006tNc79ly1g4ztt8fkmxj311s0q4ju2.jpg) 然後就是實現具體裝飾對象 `Milk` 和 `Sugar`. ![](http://ww3.sinaimg.cn/large/006tNc79ly1g4zttu0hu8j30u012edl6.jpg) 最後客戶端實現製作咖啡的動作: ![](http://ww2.sinaimg.cn/large/006tNc79ly1g4ztuahwqej316q0kkq6w.jpg) 下圖為示例的類圖: ![](http://ww4.sinaimg.cn/large/006tNc79ly1g4zsk8axpmj313e0og40u.jpg) 可以從上面例子看出,通過添加不同的材料,價格不同,並且描述信息也不一樣,並且添加順序可以動態改變,甚至不添加,讓對象變得十分靈活, 這就是裝飾者模式的精髓所在。 ## 模式使用場景 裝飾者模式同樣大量應用在 JDK 源碼中,我們經常可以看到: - JDK 的 IO 操作類, 比如 [java.io.InputStream](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html) 體系下 `FilterInputStream` 的一系列實現類,比如 `BufferedInputStream`,`LineNumberInputStream`,`DataInpuStream`。 - [java.util.Collections#synchronizedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection-java.util.Collection-) 裝飾者模式可以讓程式中對象在運行時中進行功能的增強和移除,並且採用更靈活的組合方式來進行擴展。 ## 模式的得與失 **優點**: * 在不影響其他對象的情況下,動態為單個對象新增功能。 * 裝飾類與被裝飾類 (`ConcreteComponent`) 相互獨立,互不耦合,易於擴展。 * 代替繼承方式的功能實現,減少繼承類的存在。 **缺點**: * 裝飾層次過多時會讓被裝飾的對象更複雜,不容易理解,比如使用 Java I/O 的 `DataInputStream` 對象讀取數據為 Java 基本類型值時會這樣使用: ```java DataInputStream is = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); ``` * 程式中若有太多的裝飾類,理解和使用上略有難度。 ## 結語 掌握裝飾者模式的精髓在於通過一層層的包裝,讓原來對象的功能更加強大,且包裝過程是動態的,靈活可以移除的,到最後還是會調用到原對象最原始的功能。 ## 參考 * https://www.journaldev.com/1540/decorator-design-pattern-in-java-example * https://java-design-patterns.com/patterns/decorator/ * 《設計模式之禪》:https://book.douban.com/subject/4260618/ * https://javadoop.com/post/design-pattern ## 推薦閱讀 - [掌握設計模式之適配器模式](https://mp.weixin.qq.com/s?__biz=MzI3NzEwMDAwNg==&mid=2647896284&idx=1&sn=fee2c9033e4321096d6010bee3c24a50&scene=21#wechat_redirect) - [需要介面管理的你瞭解一下?](https://mp.weixin.qq.com/s/9zrcG2Bar6VkcS8brRIx2g) - [Java 之 Lombok 必知必會](https://mp.weixin.qq.com/s?__biz=MzI3NzEwMDAwNg==&mid=2647896258&idx=1&sn=2a7ade9ebac83ec39342fdcfafea5a23&scene=21#wechat_redirect) - [Java 開發看的 Scala 入門](https://mp.weixin.qq.com/s?__biz=MzI3NzEwMDAwNg==&mid=2647896264&idx=1&sn=81f55af3a8f188b5f26eca32eb3865b8&scene=21#wechat_redirect) - [Java 微服務新生代之 Nacos](https://mp.weixin.qq.com/s?__biz=MzI3NzEwMDAwNg==&mid=2647896272&idx=1&sn=616b338b93e58afcf7303a06db307a3b&scene=21#wechat_redirect)