本篇內(nèi)容主要講解“什么是裝飾器模式”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“什么是裝飾器模式”吧!
創(chuàng)新互聯(lián)公司專注于魚峰網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供魚峰營銷型網(wǎng)站建設(shè),魚峰網(wǎng)站制作、魚峰網(wǎng)頁設(shè)計(jì)、魚峰網(wǎng)站官網(wǎng)定制、微信小程序定制開發(fā)服務(wù),打造魚峰網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供魚峰網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
裝飾器模式(Decorator Pattern)也叫作包裝器模式(Wrapper Pattern),指在不改變原有對象的基礎(chǔ)上,動態(tài)地給一個(gè)對象添加一些額外的職責(zé)。就增加功能來說,裝飾器模式相比生成子類更為靈活,屬于結(jié)構(gòu)型設(shè)計(jì)模式。
英文:
Attach additional responsibilities to an object dynamicallykeeping the same interface.Decorators provide a flexible alternativeto subclassing for extending functionality.
裝飾器模式提供了比繼承更有彈性的替代方案(擴(kuò)展原有對象的功能)將功能附加到對象上。因此,裝飾器模式的核心是功能擴(kuò)展。使用裝飾器模式可以透明且動態(tài)地?cái)U(kuò)展類的功能。
一套毛坯房,沒有裝修之前,看起來非常難看,但只要稍微裝修一番,那就漂亮多了,并且能洗澡、睡覺、做飯等,但本質(zhì)還是房子。
一輛汽車,原本就是一輛代步的車,但是瑪麗加大,配置提升,然后就成了豪車,但本質(zhì)還是一輛代步的車。
一個(gè)女生,原本很平凡,長相一般,但是經(jīng)過一番化妝,再穿點(diǎn)好看的衣服,然后就成了很多人心中的女神了。
總之,經(jīng)過點(diǎn)裝飾后,就是不一樣了,功能增強(qiáng)了。
我們還是用代碼來實(shí)現(xiàn)一把,程序員都喜歡先搞個(gè)demo,然后再慢慢研究。
//抽象組件 public abstract class Component { public abstract void operation(); } //具體組件 public class ConcreteComponent extends Component { @Override public void operation() { System.out.println("ConcreteComponent operation"); } } //裝飾器抽象 public abstract class Decorator extends Component { protected Component component; public Decorator(Component component) { this.component = component; } @Override public void operation() { component.operation(); } } //具體裝飾器 public class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); } @Override public void operation() { System.out.println("開始前搞點(diǎn)事"); super.operation(); System.out.println("結(jié)束后搞點(diǎn)事"); } } //測試 public class Client { public static void main(String[] args) { Component component = new ConcreteDecorator(new ConcreteComponent()); component.operation(); } }
運(yùn)行結(jié)果:
開始前搞點(diǎn)事 ConcreteComponent operation 結(jié)束后搞點(diǎn)事
以上便是裝飾器模式的通用代碼實(shí)現(xiàn),下面我們來分析一下。
從UML途中可以看出,其中的角色
抽象組件(Component):可以是一個(gè)接口或者抽象類,充當(dāng)被裝飾類的原始對象,規(guī)定了被裝飾對象的行為。
具體組件(ConcreteComponent):實(shí)現(xiàn)/繼承Component的一個(gè)具體對象,即被裝飾對象。
抽象裝飾器(Decorator):通用的裝飾ConcreteComponent的裝飾器,其內(nèi)部必然有一個(gè)屬性指向Component;其實(shí)現(xiàn)一般是一個(gè)抽象類,主要為了讓其子類按照其構(gòu)造形式傳入一個(gè)Component,這是強(qiáng)制的通用行為。如果系統(tǒng)中裝飾邏輯單一,則并不需要實(shí)現(xiàn)許多裝飾器,可以直接省略該類,而直接實(shí)現(xiàn)一個(gè)具體裝飾器即可。
具體裝飾器(ConcreteDecorator):Decorator的具體實(shí)現(xiàn)類,理論上,每個(gè)ConcreteDecorator都擴(kuò)展了Component對象的一種功能。
小結(jié)
裝飾器模式角色分配符合設(shè)計(jì)模式的里氏替換原則、依賴倒置原則,從而使得其具備很強(qiáng)的擴(kuò)展性,最終滿足開閉原則。
裝飾器模式的實(shí)現(xiàn)原理是,讓裝飾器實(shí)現(xiàn)與被裝飾類(例如ConcreteComponent)相同的接口(例如Component),使得裝飾器與被擴(kuò)展類類型一致,并在構(gòu)造函數(shù)中傳入該接口對象,然后在實(shí)現(xiàn)這個(gè)接口的被包裝類對象的現(xiàn)有功能上添加新功能。由于裝飾器與被包裝類屬于同一類型(均為Component),且構(gòu)造函數(shù)的參數(shù)為其實(shí)現(xiàn)接口類(Component),因此裝飾器模式具備嵌套擴(kuò)展功能,這樣就能使用裝飾器模式一層一層地對底層被包裝類進(jìn)行功能擴(kuò)展了。
在實(shí)際開發(fā)中,都會存在系統(tǒng)與系統(tǒng)之間的調(diào)用,假如說我們現(xiàn)在有個(gè)支付功能,現(xiàn)在一切都是沒問題的,但是 我們此時(shí)需要對發(fā)起支付前的請求參數(shù)和支付后的相應(yīng)參數(shù)。進(jìn)行統(tǒng)一處理,原功能不變,只是在原功能上做了一點(diǎn)擴(kuò)展(增強(qiáng))。
老功能代碼如下:
/** * @author 田先生 * @date 2021-06-02 * * 歡迎關(guān)注公眾號:java后端技術(shù)全棧 */ public interface IOrderPayService { String payment(Long orderId, BigDecimal amount); } public class OrderPayServiceImpl implements IOrderPayService { @Override public String payment(Long orderId, BigDecimal amount) { //先調(diào)用余額查詢是否足夠 System.out.println("發(fā)起支付,訂單號:" + orderId + ", 支付金額:" + amount.toString()); //調(diào)用支付系統(tǒng) String result = "訂單id=" + orderId + "支付完成"; System.out.println("支付結(jié)果:" + result); return result; } } public class OrderClient { public static void main(String[] args) { IOrderPayService orderPayService = new OrderPayServiceImpl(); orderPayService.payment(10001L,new BigDecimal("5000")); } }
運(yùn)行輸出:
發(fā)起支付,訂單號:10001, 支付金額:5000 支付結(jié)果:訂單id=10001支付完成
新需求,需要把這些請求參數(shù)和相應(yīng)結(jié)果進(jìn)行單獨(dú)搜集處理,此時(shí)為了不影響原有功能,于是我們可以對其進(jìn)行功能增強(qiáng)。
/** * @author 田先生 * @date 2021-06-02 * * 歡迎關(guān)注公眾號:java后端技術(shù)全棧 */ public class OrderPayDecorator implements IOrderPayService { private IOrderPayService orderPayService; public OrderPayDecorator(IOrderPayService orderPayService) { this.orderPayService = orderPayService; } @Override public String payment(Long orderId, BigDecimal amount) { System.out.println("把這個(gè)訂單信息(發(fā)起支付)" + "訂單id=" + orderId + "支付金額=" + amount.toString() + " 【發(fā)送給MQ】"); String result = orderPayService.payment(orderId, amount); System.out.println("把訂單支付結(jié)果信息" + result + " 【發(fā)送給MQ】"); return result; } } public class OrderClient { public static void main(String[] args) { IOrderPayService orderPayService =new OrderPayDecorator(new OrderPayServiceImpl()); orderPayService.payment(10001L,new BigDecimal("5000")); } }
運(yùn)行輸出:
把這個(gè)訂單信息(發(fā)起支付)訂單id=10001支付金額=5000 【發(fā)送給MQ】 發(fā)起支付,訂單號:10001, 支付金額:5000 支付結(jié)果:訂單id=10001支付完成 把訂單支付結(jié)果信息訂單id=10001支付完成 【發(fā)送給MQ】
整個(gè)過程,大家有沒有發(fā)現(xiàn),我們并沒動原有的代碼,僅僅只是做了功能增強(qiáng)。
裝飾器模式在新項(xiàng)目中基本上不會用到,通常都是在老項(xiàng)目中使用,因?yàn)橐延械墓δ懿蛔?,只是做了一些功能增?qiáng)。
裝飾器設(shè)計(jì)模式在JDK源碼、Spring源碼以及Mybatis源碼中都有。
JDK源碼中
裝飾器模式比較經(jīng)典的應(yīng)用就是 JDK 中的 java.io 包下,InputStream、OuputStream、Reader、Writer 及它們的子類。
以 InputStream 為例
FileInputStream 是 InputStream 的子類,用來讀取文件字節(jié)流
BufferedInputStream 是 InputStream 的子類的子類,可緩存的字節(jié)流
DataInputStream 也是 InputStream 的子類的子類,可直接讀取 Java 基本類型的字節(jié)流
UML圖
DataInputStream 中構(gòu)造器入?yún)⒈闶亲约旱母割?InputStream)。
如果希望提供一個(gè)可以讀取文件 + 可緩存的字節(jié)流,使用繼承方式,就需要派生 FileBufferedInputStream;
如果希望提供一個(gè)可以讀取文件 + 直接讀取基本類型的字節(jié)流,使用繼承方式,就需要派生 FileDataInputStream。
字節(jié)流功能的增強(qiáng)還包括支持管道 pipe、字節(jié)數(shù)組 bytearray、字節(jié)對象 object、字節(jié)流字符流的轉(zhuǎn)換 等維度,如果用繼承方式,那類的層級與種類會多到爆炸。
為了解決問題,這邊就使用了裝飾器模式。
Spring源碼中
在Spring中,我們可以嘗試?yán)斫庖幌耇ransactionAwareCacheDecorator類,這個(gè)類主要用來處理事務(wù)緩存,代碼如下。
public class TransactionAwareCacheDecorator implements Cache { private final Cache targetCache; //構(gòu)造方法入?yún)㈩愋蜑樽约旱母割悾ń涌陬愋停?nbsp; public TransactionAwareCacheDecorator(Cache targetCache) { Assert.notNull(targetCache, "Target Cache must not be null"); this.targetCache = targetCache; } public Cache getTargetCache() { return this.targetCache; } //... }
TransactionAwareCacheDecorator就是對Cache的一個(gè)包裝,因此,這里也是使用了裝飾器模式。
Mybatis源碼中
MyBatis中關(guān)于Cache和CachingExecutor接口的實(shí)現(xiàn)類也使用了裝飾者設(shè)計(jì)模式。Executor是MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負(fù)責(zé)SQL語句的生成和查詢緩存的維護(hù);CachingExecutor是一個(gè)Executor的裝飾器,給一個(gè)Executor增加了緩存的功能。此時(shí)可以看做是對Executor類的一個(gè)增強(qiáng),故使用裝飾器模式是合適的。
在CachingExecutor 中
public class CachingExecutor implements Executor { //持有組件對象 private Executor delegate; private TransactionalCacheManager tcm = new TransactionalCacheManager(); //構(gòu)造方法,傳入組件對象 public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } @Override public int update(MappedStatement ms, Object parameterObject) throws SQLException { //轉(zhuǎn)發(fā)請求給組件對象,可以在轉(zhuǎn)發(fā)前后執(zhí)行一些附加動作 flushCacheIfRequired(ms); return delegate.update(ms, parameterObject); } //... }
看完裝飾器模式后,你是否有感覺,裝飾器模式和代理模式非常的相像,下面我們就來做個(gè)對比。
1.裝飾器模式可以理解為一種特殊的代理模式。
2.裝飾器模式強(qiáng)調(diào)自身的功能擴(kuò)展,透明的擴(kuò)展(即用戶想增強(qiáng)什么功能就增強(qiáng)什么功能),可動態(tài)定制的擴(kuò)展。
3.代理模式強(qiáng)調(diào)的是代理過程的控制。
優(yōu)點(diǎn)
裝飾器是繼承的有力補(bǔ)充,比繼承靈活,在不改變原有對象的情況下,動態(tài)地給一個(gè)對象擴(kuò)展功能,即插即用。
通過使用不同裝飾類及這些裝飾類的排列組合,可以實(shí)現(xiàn)不同效果。
裝飾器模式完全遵守開閉原則。
缺點(diǎn)
會出現(xiàn)更多的代碼、更多的類,增加程序的復(fù)雜性。
動態(tài)裝飾在多層裝飾時(shí)會更復(fù)雜。
到此,相信大家對“什么是裝飾器模式”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!