裝飾模式 應用場景和實現


有個大神寫的很好:

參考:設計模式學習筆記(四:策略模式)

參考:設計模式學習筆記(二:觀察者模式)

參考:設計模式學習筆記-代理模式

參考:設計模式--裝飾者模式與代理模式(重要)

參考:設計模式——代理模式與裝飾模式的異同 (重要)

參考:設計模式之裝飾模式

參考:java模式—裝飾者模式

參考:修飾者模式(裝飾者模式,Decoration) 

 

裝飾者(decorator)模式:在不改變對象自身的基礎上,在程序運行期間給對像動態的添加職責。與繼承相比,裝飾者是一種更輕便靈活的做法。
裝飾者模式的特點
可以動態的給某個對象添加額外的職責,而不會影響從這個類中派生的其它對象;

(1)應用場景:

(a)在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責。

  (b)  需要動態地給一個對象增加功能,這些功能也可以動態地被撤銷。  當不能采用繼承的方式對系統進行擴充或者采用繼承不利於系統擴展和維護時。

(2)實現的例子:

最常見的就是輸入輸出流:

 BufferedReader in1 = new BufferedReader(new InputStreamReader(new FileInputStream(file)));//字符流
 DataInputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));//字節流
  // DataInputStream-從數據流讀取字節,並將它們裝換為正確的基本類型值或字符串
  // BufferedInputStream-可以通過減少讀寫次數來提高輸入和輸出的速度

 

從例子開始講解,比如今天你要出門約會,你肯定是要決定好你要穿什么樣的衣服出門,用衣服來裝飾下自己,讓自己擁有一個完美的約會。比如,你會穿一件襯衫,然后穿一件西服褲,最后穿皮鞋出門。這就是裝飾者模式的一個例子。為了實現這個功能,會有下面的代碼。
public class People {
 
public void wearShirt(){
 
System.out.println("******穿襯衫******");
 
}
public void wearTrouser(){
 
System.out.println("******穿西服褲******");
 
}
 
 
 
public void wearShoes(){
 
System.out.println("******穿皮鞋******");
 
}
 
}

 

 
public class Client { 
public static void main(String[] args) {
 
People people = new People();
 
people.wearShirt();
 
people.wearTrouser();
 
people.wearShoes();
 
 } 
}

 

雖然上面的代碼實現了出門穿衣服的功能,但是這里會問題。如果我現在要增加一個功能,我要先穿襪子,再穿皮鞋。此時就要在People類中增加一個穿襪子的方法。這就違背了設計模式的開閉原則,所謂開閉原則就是類可以進行擴展,但是不可以進行修改。所以就有如下的設計:
public interface People {
public void wearClothing(); 
}
public class Xiaoming implements People{ 
private String name; 
public Xiaoming(){
 
name = "小明";
 
} 
public void wearClothing(){
 
System.out.println(name+"******開始穿衣服******");
 
}
 
public String getName() {
 
return name;
 
}
 
}
public abstract class Finery implements People { 
protected People people; 
public Finery(People people){
 
this.people = people;
 
}
 
public abstract void wearClothing();
 
}
public class ShirtFinery extends Finery {
 
public ShirtFinery(People people){
 
super(people);
 
}
 
@Override
 
public void wearClothing() {
 
people.wearClothing();
 
System.out.println("******穿襯衫******");
 
} 
 
}
public class ShoesFinery extends Finery { 
public ShoesFinery(People people){
 
super(people);
 
}
 
@Override
 
public void wearClothing() {
 
people.wearClothing();
 
System.out.println("******穿皮鞋*******");
 
} 
}
 
public class TrouserFinery extends Finery {
 
 
 
public TrouserFinery(People people){
 
super(people);
 
}
 
@Override
 
public void wearClothing() {
 
people.wearClothing();
 
System.out.println("******穿西服褲*******");
 
} 
}
public class Client { 
 
public static void main(String[] args) {
 
People people = new Xiaoming();
 
Finery shirtFinery = new ShirtFinery(people);
 
Finery trouserFinery = new TrouserFinery(shirtFinery);
 
Finery shoesFinery = new ShoesFinery(trouserFinery);
 
shoesFinery.wearClothing();
 
}
 
 
 
}

  (3)類圖:

People是定義了一個接口,用來添加具體的職責,而Xiaoming是具體的People,也就是被裝飾的對象。對於Finery實現了People接口,進而對People進行擴展,而Finery的子類就是具體的裝飾類,Finery中依賴People類,裝飾的具體對象。 這就是所謂的裝飾者模式。如果我要添加穿襪子的步驟,則只需要再添加一個實現類,完全不需要修改其他代碼(Client是客戶端類,肯定是要修改的)。

 

裝飾者模式和代理模式的區別:

代理模式可以參考:Java設計模式之代理模式(靜態代理和JDK、CGLib動態代理)以及應用場景

兩種模式的特點


裝飾模式:

  在不改變接口的前提下,動態擴展對象的訪問。
  動態繼承,讓類具有在運行期改變行為的能力。
  裝飾模式,突出的是運行期增加行為,這和繼承是不同的,繼承是在編譯期增加行為。
  強調:增強,新增行為

代理模式:

  在不改變接口的前提下,控制對象的訪問。
  1.從封裝的角度講,是為了解決類與類之間相互調用而由此導致的耦合關系,可以說是接口的另外一個層引用。
    比如:在a類->b代理->c類這個關系中,c類的一切行為都隱藏在b中。即調用者不知道要訪問的內容與代理了什么對象。
  2.從復用的角度講,可以解決不同類調用一個復雜類時,僅僅因較小的改變而導致整個復雜類新建一個類。
    比如:a類->c類1;b類->c類2。
    可以變為a類->ca代理類->c類;b類->cb代理類-c類。
  代理模式,是類之間的封裝和(某方面的)復用。
  強調:限制,控制訪問

 

總結:

  1. 裝飾模式可以讓使用者直觀的看到增強了哪些功能,而代理模式完全限制了使用者。

  2. 對裝飾模式來說,裝飾者(Decorator)和被裝飾者(Cafe)都實現同一個 接口。

  3. 對代理模式來說,代理類(Proxy Class)和真實處理的類(Real Class)都實現同一個接口。

  4. 此外,不論我們使用哪一個模式,都可以很容易地在真實對象的方法前面或者后面加上自定義的方法。

裝飾模式與繼承的比較


  明顯的,裝飾模式可以動態的擴展對象的行為。

  比如某對象有30項行為,但是在第一階段用到1-20行為,第二階段用到11-30項行為,所以這時候,就可以只定義11-20的行為。

  在第一階段運行時,可以將1-10的行為以“裝飾1”給加上

  到第二階段運行時,可以將“裝飾1”去掉,將21-30的能以“裝飾2”給加上。

  但是繼承是在編譯期增加行為。

裝飾模式的優缺點


優點:

  1. 裝飾模式可以提供比繼承更多地靈活性。
  2. 可以通過一種動態的方式來擴展一個對象的功能,在運行時選擇不同的裝飾器,從而實現不同的行為。
  3. 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合。可以使用多個具體裝飾類來裝飾同一對象,得到功能更為強大的對象。
  4. 具體構件類與具體裝飾類可以獨立變化,用戶可以根據需要增加新的具體構件類和具體裝飾類,在使用時再對其進行組合,原有代碼無須改變,符合“開閉原則”。

缺點:

  1. 會產生很多的小對象(具體裝飾類),增加了系統的復雜性。
  2. 這種比繼承更加靈活機動的特性,也同時意味着裝飾模式比繼承易於出錯,排錯也很困難,對於多次裝飾的對象,調試時尋找錯誤可能需要逐級排查,較為煩瑣。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM