自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

3年工作必備 裝飾器模式

開(kāi)發(fā) 前端
裝飾器模式(Decorator Pattern)也叫作包裝器模式(Wrapper Pattern),指在不改變?cè)袑?duì)象的基礎(chǔ)上,動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。就增加功能來(lái)說(shuō),裝飾器模式相比生成子類更為靈活,屬于結(jié)構(gòu)型設(shè)計(jì)模式。

[[403421]]

 今天我給大家分享設(shè)計(jì)模式中的裝飾器模式。用貼切的生活故事,以及真實(shí)項(xiàng)目場(chǎng)景來(lái)講設(shè)計(jì)模式,最后用一句話來(lái)總結(jié)這個(gè)設(shè)計(jì)模式。

故事

古話說(shuō)的好:人靠衣裳馬靠鞍。下面先帶大家來(lái)熟悉這句話的背景:

人靠衣裝馬靠鞍,狗配鈴鐺跑的歡出自沈自晉《望湖亭記》第十出:“雖然如此,佛靠金裝,人靠衣裝,打扮也是很要緊的。”《醒世恒言》卷一?兩縣令競(jìng)義婚孤女:”常言道:’佛是金裝,人是衣裝,世人眼孔淺的多,只有皮相,沒(méi)有骨相。’”俗語(yǔ)我們會(huì)說(shuō)成人靠衣裝馬靠鞍。

這個(gè)經(jīng)典故事,讓我想起了一個(gè)設(shè)計(jì)模式:裝飾器模式。

什么是裝飾器模式呢?請(qǐng)聽(tīng)老田慢慢道來(lái)。

裝飾器模式概述

裝飾器模式(Decorator Pattern)也叫作包裝器模式(Wrapper Pattern),指在不改變?cè)袑?duì)象的基礎(chǔ)上,動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。就增加功能來(lái)說(shuō),裝飾器模式相比生成子類更為靈活,屬于結(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ò)展原有對(duì)象的功能)將功能附加到對(duì)象上。因此,裝飾器模式的核心是功能擴(kuò)展。使用裝飾器模式可以透明且動(dòng)態(tài)地?cái)U(kuò)展類的功能。

生活中的案例

一套毛坯房,沒(méi)有裝修之前,看起來(lái)非常難看,但只要稍微裝修一番,那就漂亮多了,并且能洗澡、睡覺(jué)、做飯等,但本質(zhì)還是房子。

一輛汽車,原本就是一輛代步的車,但是瑪麗加大,配置提升,然后就成了豪車,但本質(zhì)還是一輛代步的車。

一個(gè)女生,原本很平凡,長(zhǎng)相一般,但是經(jīng)過(guò)一番化妝,再穿點(diǎn)好看的衣服,然后就成了很多人心中的女神了。

總之,經(jīng)過(guò)點(diǎn)裝飾后,就是不一樣了,功能增強(qiáng)了。

裝飾器模式通用代碼實(shí)現(xiàn)

我們還是用代碼來(lái)實(shí)現(xiàn)一把,程序員都喜歡先搞個(gè)demo,然后再慢慢研究。

  1. //抽象組件 
  2. public abstract class Component { 
  3.     public abstract void operation(); 
  4. //具體組件 
  5. public class ConcreteComponent extends Component { 
  6.     @Override 
  7.     public void operation() { 
  8.         System.out.println("ConcreteComponent operation"); 
  9.     } 
  10. //裝飾器抽象 
  11. public abstract class Decorator extends Component { 
  12.  
  13.     protected Component component; 
  14.  
  15.     public Decorator(Component component) { 
  16.         this.component = component; 
  17.     } 
  18.  
  19.     @Override 
  20.     public void operation() { 
  21.         component.operation(); 
  22.     } 
  23. //具體裝飾器 
  24. public class ConcreteDecorator extends Decorator { 
  25.     public ConcreteDecorator(Component component) { 
  26.         super(component); 
  27.     } 
  28.  
  29.     @Override 
  30.     public void operation() { 
  31.         System.out.println("開(kāi)始前搞點(diǎn)事"); 
  32.         super.operation(); 
  33.         System.out.println("結(jié)束后搞點(diǎn)事"); 
  34.     } 
  35. //測(cè)試 
  36. public class Client { 
  37.     public static void main(String[] args) { 
  38.         Component component = new ConcreteDecorator(new ConcreteComponent()); 
  39.         component.operation(); 
  40.     } 

運(yùn)行結(jié)果:

  1. 開(kāi)始前搞點(diǎn)事 
  2. ConcreteComponent operation 
  3. 結(jié)束后搞點(diǎn)事 

以上便是裝飾器模式的通用代碼實(shí)現(xiàn),下面我們來(lái)分析一下。

裝飾器模式UML圖

從UML途中可以看出,其中的角色

裝飾器模式中的角色

  • 抽象組件(Component):可以是一個(gè)接口或者抽象類,充當(dāng)被裝飾類的原始對(duì)象,規(guī)定了被裝飾對(duì)象的行為。
  • 具體組件(ConcreteComponent):實(shí)現(xiàn)/繼承Component的一個(gè)具體對(duì)象,即被裝飾對(duì)象。
  • 抽象裝飾器(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對(duì)象的一種功能。

小結(jié)

裝飾器模式角色分配符合設(shè)計(jì)模式的里氏替換原則、依賴倒置原則,從而使得其具備很強(qiáng)的擴(kuò)展性,最終滿足開(kāi)閉原則。

裝飾器模式的實(shí)現(xiàn)原理是,讓裝飾器實(shí)現(xiàn)與被裝飾類(例如ConcreteComponent)相同的接口(例如Component),使得裝飾器與被擴(kuò)展類類型一致,并在構(gòu)造函數(shù)中傳入該接口對(duì)象,然后在實(shí)現(xiàn)這個(gè)接口的被包裝類對(duì)象的現(xiàn)有功能上添加新功能。由于裝飾器與被包裝類屬于同一類型(均為Component),且構(gòu)造函數(shù)的參數(shù)為其實(shí)現(xiàn)接口類(Component),因此裝飾器模式具備嵌套擴(kuò)展功能,這樣就能使用裝飾器模式一層一層地對(duì)底層被包裝類進(jìn)行功能擴(kuò)展了。

實(shí)戰(zhàn)

在實(shí)際開(kāi)發(fā)中,都會(huì)存在系統(tǒng)與系統(tǒng)之間的調(diào)用,假如說(shuō)我們現(xiàn)在有個(gè)支付功能,現(xiàn)在一切都是沒(méi)問(wèn)題的,但是 我們此時(shí)需要對(duì)發(fā)起支付前的請(qǐng)求參數(shù)和支付后的相應(yīng)參數(shù)。進(jìn)行統(tǒng)一處理,原功能不變,只是在原功能上做了一點(diǎn)擴(kuò)展(增強(qiáng))。

老功能代碼如下:

  1. /** 
  2.  * @author 田先生 
  3.  * @date 2021-06-02 
  4.  * 
  5.  * 歡迎關(guān)注公眾號(hào):java后端技術(shù)全棧 
  6.  */ 
  7. public interface IOrderPayService { 
  8.     String payment(Long orderId, BigDecimal amount); 
  9. public class OrderPayServiceImpl implements IOrderPayService { 
  10.  
  11.     @Override 
  12.     public String payment(Long orderId, BigDecimal amount) { 
  13.         //先調(diào)用余額查詢是否足夠 
  14.         System.out.println("發(fā)起支付,訂單號(hào):" + orderId + ", 支付金額:" + amount.toString()); 
  15.         //調(diào)用支付系統(tǒng) 
  16.         String result = "訂單id=" + orderId + "支付完成"
  17.         System.out.println("支付結(jié)果:" + result); 
  18.         return result; 
  19.     } 
  20. public class OrderClient { 
  21.     public static void main(String[] args) { 
  22.         IOrderPayService orderPayService = new OrderPayServiceImpl(); 
  23.         orderPayService.payment(10001L,new BigDecimal("5000")); 
  24.     } 

運(yùn)行輸出:

  1. 發(fā)起支付,訂單號(hào):10001, 支付金額:5000 
  2. 支付結(jié)果:訂單id=10001支付完成 

新需求,需要把這些請(qǐng)求參數(shù)和相應(yīng)結(jié)果進(jìn)行單獨(dú)搜集處理,此時(shí)為了不影響原有功能,于是我們可以對(duì)其進(jìn)行功能增強(qiáng)。

  1. /** 
  2.  * @author 田先生 
  3.  * @date 2021-06-02 
  4.  * 
  5.  * 歡迎關(guān)注公眾號(hào):java后端技術(shù)全棧 
  6.  */ 
  7. public class OrderPayDecorator implements IOrderPayService { 
  8.  
  9.     private IOrderPayService orderPayService; 
  10.  
  11.     public OrderPayDecorator(IOrderPayService orderPayService) { 
  12.         this.orderPayService = orderPayService; 
  13.     } 
  14.  
  15.     @Override 
  16.     public String payment(Long orderId, BigDecimal amount) { 
  17.         System.out.println("把這個(gè)訂單信息(發(fā)起支付)" + "訂單id=" + orderId + "支付金額=" + amount.toString() + " 【發(fā)送給MQ】"); 
  18.         String result = orderPayService.payment(orderId, amount); 
  19.         System.out.println("把訂單支付結(jié)果信息" + result + " 【發(fā)送給MQ】"); 
  20.         return result; 
  21.     } 
  22. public class OrderClient { 
  23.     public static void main(String[] args) { 
  24.         IOrderPayService orderPayService =new OrderPayDecorator(new OrderPayServiceImpl()); 
  25.         orderPayService.payment(10001L,new BigDecimal("5000")); 
  26.     } 

運(yùn)行輸出:

  1. 把這個(gè)訂單信息(發(fā)起支付)訂單id=10001支付金額=5000 【發(fā)送給MQ】 
  2. 發(fā)起支付,訂單號(hào):10001, 支付金額:5000 
  3. 支付結(jié)果:訂單id=10001支付完成 
  4. 把訂單支付結(jié)果信息訂單id=10001支付完成 【發(fā)送給MQ】 

整個(gè)過(guò)程,大家有沒(méi)有發(fā)現(xiàn),我們并沒(méi)動(dòng)原有的代碼,僅僅只是做了功能增強(qiáng)。

裝飾器模式在新項(xiàng)目中基本上不會(huì)用到,通常都是在老項(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 的子類,用來(lái)讀取文件字節(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é)對(duì)象 object、字節(jié)流字符流的轉(zhuǎn)換 等維度,如果用繼承方式,那類的層級(jí)與種類會(huì)多到爆炸。

為了解決問(wèn)題,這邊就使用了裝飾器模式。

Spring源碼中

在Spring中,我們可以嘗試?yán)斫庖幌耇ransactionAwareCacheDecorator類,這個(gè)類主要用來(lái)處理事務(wù)緩存,代碼如下。

  1. public class TransactionAwareCacheDecorator implements Cache { 
  2.     private final Cache targetCache; 
  3.     //構(gòu)造方法入?yún)㈩愋蜑樽约旱母割悾ń涌陬愋停?nbsp;
  4.     public TransactionAwareCacheDecorator(Cache targetCache) { 
  5.         Assert.notNull(targetCache, "Target Cache must not be null"); 
  6.         this.targetCache = targetCache; 
  7.     } 
  8.  
  9.     public Cache getTargetCache() { 
  10.         return this.targetCache; 
  11.     } 
  12.     //... 

TransactionAwareCacheDecorator就是對(duì)Cache的一個(gè)包裝,因此,這里也是使用了裝飾器模式。

Mybatis源碼中

MyBatis中關(guān)于Cache和CachingExecutor接口的實(shí)現(xiàn)類也使用了裝飾者設(shè)計(jì)模式。Executor是MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負(fù)責(zé)SQL語(yǔ)句的生成和查詢緩存的維護(hù);CachingExecutor是一個(gè)Executor的裝飾器,給一個(gè)Executor增加了緩存的功能。此時(shí)可以看做是對(duì)Executor類的一個(gè)增強(qiáng),故使用裝飾器模式是合適的。

在CachingExecutor 中

  1. public class CachingExecutor implements Executor { 
  2.   //持有組件對(duì)象 
  3.   private Executor delegate; 
  4.   private TransactionalCacheManager tcm = new TransactionalCacheManager(); 
  5.     //構(gòu)造方法,傳入組件對(duì)象 
  6.   public CachingExecutor(Executor delegate) { 
  7.     this.delegate = delegate; 
  8.     delegate.setExecutorWrapper(this); 
  9.   } 
  10.   @Override 
  11.   public int update(MappedStatement ms, Object parameterObject) throws SQLException { 
  12.       //轉(zhuǎn)發(fā)請(qǐng)求給組件對(duì)象,可以在轉(zhuǎn)發(fā)前后執(zhí)行一些附加動(dòng)作 
  13.     flushCacheIfRequired(ms); 
  14.     return delegate.update(ms, parameterObject); 
  15.   } 
  16.   //... 
  17.  } 

總結(jié)

看完裝飾器模式后,你是否有感覺(jué),裝飾器模式和代理模式非常的相像,下面我們就來(lái)做個(gè)對(duì)比。

1.裝飾器模式可以理解為一種特殊的代理模式。

2.裝飾器模式強(qiáng)調(diào)自身的功能擴(kuò)展,透明的擴(kuò)展(即用戶想增強(qiáng)什么功能就增強(qiáng)什么功能),可動(dòng)態(tài)定制的擴(kuò)展。

3.代理模式強(qiáng)調(diào)的是代理過(guò)程的控制。

優(yōu)點(diǎn)

  • 裝飾器是繼承的有力補(bǔ)充,比繼承靈活,在不改變?cè)袑?duì)象的情況下,動(dòng)態(tài)地給一個(gè)對(duì)象擴(kuò)展功能,即插即用。
  • 通過(guò)使用不同裝飾類及這些裝飾類的排列組合,可以實(shí)現(xiàn)不同效果。
  • 裝飾器模式完全遵守開(kāi)閉原則。

缺點(diǎn)

  • 會(huì)出現(xiàn)更多的代碼、更多的類,增加程序的復(fù)雜性。
  • 動(dòng)態(tài)裝飾在多層裝飾時(shí)會(huì)更復(fù)雜。
  • 好了,今天的分享就到此結(jié)束,希望你能徹底掌握裝飾器模式,如果還有疑問(wèn),或者技術(shù)探討之類的,歡迎加我微信,一起探討。

文轉(zhuǎn)載自微信公眾號(hào)「Java后端技術(shù)全?!?,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java后端技術(shù)全棧公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: Java后端技術(shù)全棧
相關(guān)推薦

2023-09-04 13:14:00

裝飾器設(shè)計(jì)模式

2023-12-13 13:28:16

裝飾器模式Python設(shè)計(jì)模式

2022-01-19 08:21:12

設(shè)計(jì)裝飾器模式

2025-01-22 15:58:46

2022-09-14 08:16:48

裝飾器模式對(duì)象

2024-02-23 12:11:53

裝飾器模式對(duì)象

2024-04-10 12:27:43

Python設(shè)計(jì)模式開(kāi)發(fā)

2021-07-12 10:24:36

Go裝飾器代碼

2020-12-01 07:16:05

重學(xué)設(shè)計(jì)模式

2010-02-01 17:50:32

Python裝飾器

2022-03-25 11:01:28

Golang裝飾模式Go 語(yǔ)言

2022-12-31 19:20:15

JavaScriptstage 3裝飾器

2023-02-07 07:47:52

Python裝飾器函數(shù)

2009-12-18 10:47:16

Ruby裝飾模式

2022-11-26 00:00:06

裝飾者模式Component

2023-08-29 08:44:38

裝飾器組合模式

2024-02-26 00:00:00

TypeScript裝飾器decorators

2011-10-11 10:05:09

微軟免費(fèi)服務(wù)器

2021-06-02 08:17:05

門面模式設(shè)計(jì)

2011-10-12 10:07:52

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)