深入探索Java設計模式(三)之裝飾器模式
裝飾器模式使你可以在執行時使用類似於物件組成的技術來裝飾類。這在我們希望例項化具有新職責的物件而無需對基礎類進行任何程式碼更改的情況下尤其有用。本文是在學習完優銳課JAVA架構VIP課程—【框架原始碼專題】中《學習原始碼中的優秀設計模式》後寫下的學習感悟。探討了這種模式,並向你展示瞭如何使用提供的Java程式碼示例來實現它。
深入探索Java設計模式(一)之單例模式
深入探索Java設計模式(二)之策略模式
總覽
裝飾器模式是“四人幫”(Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides)建立的結構設計模式之一。 通過解決在執行時向物件新增新狀態或行為的反覆出現的問題,它在面向物件的設計中利用了可重用性和靈活性。儘管這種想法與面向物件原理(稱為繼承)押韻,但簡單繼承在這種情況下不適用,因為它是靜態的並且考慮到整個類。這為新方法鋪平了道路,因為它著重於提供靈活的替代方法來擴充套件子類功能,因此可以動態地向物件施加新的責任。
裝飾器模式
顧名思義,裝飾器模式可配置特定物件的功能。這是Java原始IO類中的一種常見模式,用於對JVM之外的源進行讀寫。例如,InputStream和OutputStream類及其子類在其讀取和寫入操作中廣泛使用此模式。可以連結這些類的實現,以有效地從不同的源(例如本機檔案系統)讀取和寫入資料。
例如,請注意抽象類OutputStream。它有一個稱為read的抽象方法,該方法必須由子類定義。可以重寫該方法以擴充套件其行為,以更改或增強其效率。在將職責委派給另一個OutputStream之前,OutputStream實現會執行所需的工作。實現鏈將另一個OutputStream用作建構函式引數。具體的子類,例如OutputStream的FileOutputStream或SocketOutputStream,是管道的末尾,最終寫入資料,而沒有將寫入責任委託給另一個流。因此,在其建構函式引數中不需要另一個OutputStream物件。
讓我們看看在Java IO類的OutputStream類中使用裝飾器模式如何導致在將位元組陣列寫入磁碟之前將多個操作連結在一起。
1 @Test 2 public void testingDecoratorPattern() throws IOException { 3 final File file = new File("myfile.dat"); 4 final FileOutputStream fileOutputStream = new 5 FileOutputStream(f); 6 final BufferedOutputStream bufferedOutputStream = new 7 BufferedOutputStream(fileOutputStream); 8 final ObjectOutputStream objectOutputStream = new 9 ObjectOutputStream(fileOutputStream); 10 objectOutputStream.writeBoolean(true); 11 objectOutputStream.writeInt(12); 12 objectOutputStream.writeObject(new ArrayList<Integer>()); 13 objectOutputStream.flush(); 14 objectOutputStream.close(); 15 bufferedOutputStream.close(); 16 fileOutputStream.close(); 17 assertTrue(file.exists()); 18 }
FileOutputStream物件用於將檔案寫入磁碟。 BufferedOutputStream物件快取對寫操作的所有呼叫,並一次寫入幾個位元組。 這可以提高效率,特別是在寫入磁碟時。ObjectOutputStream中的物件序列化的內建機制將物件和原始型別寫入流中。但是,ObjectOutputStream不知道將檔案寫入何處。這主要用於將責任委託給另一個OutputStream。
裝飾器模式的使用
假設我們要建立一個類設計,但要增加新功能。 在這種情況下,裝飾器模式可為客戶端提供所需的功能組合,以增強進一步的實現,如圖1所示。
圖1:裝飾器模式
在這裡,茶是一個抽象類,由產品的所有類別或變體子類化。在包含有關其型別的特定資訊的每個子類中都設定了detailsinstance變數。getDetails()方法返回有關其型別的資訊。父類將price() 方法宣告為抽象,子類將定義其自己的實現。除了可用的茶型別以外,我們還可以通過多種變體和組合實現任意數量的茶。這是裝飾器模式的關鍵。
實現裝飾器模式
現在,讓我們看看如何實現裝飾器模式。該示例非常簡單,不言自明。這些類的佈局如下。
1 package org.mano.example; 2 public interface Car { 3 void paint(); 4 } 5 6 package org.mano.example; 7 public class ElectricCar implements Car { 8 @Override 9 public void paint() { 10 // ... 11 } 12 } 13 14 package org.mano.example; 15 public class HybridCar implements Car { 16 @Override 17 public void paint() { 18 // ... 19 } 20 } 21 22 package org.mano.example; 23 public abstract class CarDecorator implements Car { 24 protected Car decoratedCar; 25 public CarDecorator(Car car){ 26 decoratedCar = car; 27 } 28 public void paint(){ 29 decoratedCar.paint(); 30 } 31 } 32 33 package org.mano.example; 34 public class CarColorDecorator extends CarDecorator { 35 public CarColorDecorator(Car car) { 36 super (car); 37 } 38 @Override 39 public void paint(){ 40 decoratedCar.paint(); 41 setTheme(decoratedCar); 42 } 43 private void setTheme(Car car){ 44 // ... 45 } 46 47 } 48 49 package org.mano.example; 50 public class DPApp { 51 public static void main(String[] args) { 52 Car defaultHybridCar = new HybridCar(); 53 Car redHybridCar = new CarColorDecorator(new 54 HybridCar()); 55 Car blueElectricCar = new CarColorDecorator(new 56 ElectricCar()); 57 defaultHybridCar.paint(); 58 redHybridCar.paint(); 59 blueElectricCar.paint(); 60 } 61 }
結論
這是OOP中最常用的模式之一。這種模式的強大之處在於將物件組合在一起,例如ObjectOutputStream,BufferedOutputStream和FileOutputStream擴充套件了抽象超類OutputStream。每個子類建構函式都將OutputStream物件作為引數。沒有裝飾器模式,這將是不可能的。否則,我們必須建立許多類來獲取本質,這將是一個糟糕的設計。
感謝閱讀!歡迎留言。想更深入探討學習也歡迎私信我。下篇繼