2009-10-11 65 views
47

我正在考慮我的設計模式,我在編碼中尚未認真使用的一種模式是裝飾模式。何時使用裝飾模式?

我明白這個模式,但是我很想知道的是在現實世界中的一些很好的具體例子,裝飾模式是最好的/最優的/優雅的解決方案。特定的情況下,需要裝飾模式是非常方便的。

謝謝。

回答

31

Decorator模式是使用了很多與流:你可以用用的流的獲取附加功能。我已經看到了.Net框架 - 據我所知,這發生在其他地方。我最喜歡在FileStream中使用GZipStream來增加壓縮。

+16

同樣,Java流只是裝飾裝飾者的巨大集合,裝飾者裝飾了裝飾物。我的臉剛好融化了。 – 2009-10-11 04:28:56

+6

我認爲溪流會比我頭上的咖啡例子更好的例子:我書桌上的設計模式書。 – 2009-10-11 05:01:02

+1

任何人都可以解釋爲什麼裝飾模式是這種情況的最佳解決方案? – 2016-05-13 03:59:20

3

Zend框架使用表單元素

一些更多的信息裝飾:http://framework.zend.com/manual/en/zend.form.decorators.html

+0

是的,我可以看到如何使用它的形式元素是有道理的。我正在閱讀維基百科頁面上的類似示例:http://en.wikipedia.org/wiki/Decorator_Pattern – 2009-10-11 03:18:20

+0

很高興看到裝飾設計模式示例,它始終不是流包裝類示例! – kta 2014-04-13 13:53:05

20

我最近使用的裝飾圖案,其中使用以下CommandProcessor接口的web服務:

public Command receive(Request request); 
public Response execute(Command command); 
public void respond(Response response); 

基本上,CommandProcessor接收到一個請求,並創建正確的命令,執行命令,並創建相應的響應併發送響應。當我想添加時間並記錄它時,我創建了一個使用現有CommandProcessor作爲其組件的TimerDecorator。 TimerDecorator實現CommandProcessor接口,但只是添加時序,然後調用它的目標,這是真正的CommandProcessor。事情是這樣的:

public class TimerDecorator implements CommandProcessor { 
    private CommandProcessor target; 
    private Timer timer; 

    public TimerDecorator(CommandProcessor processor) { 
     this.target = processor; 
     this.timer = new Timer(); 
    } 

    public Command receive(Request request) { 
     this.timer.start(); 
     return this.target.receive(request); 
    } 

    public Response execute(Command command) { 
     return this.target.execute(command); 
    } 

    public void respond(Response response) { 
     this.target.response(response); 
     this.timer.stop(); 
     // log timer 
    } 

} 

所以真正的CommandProcessor被包裹在裏面TimerDecorator,我可以把TimerDecorator就像目標CommandProcessor,但現在定時邏輯已被添加。

60

裝飾模式用於添加額外的功能到一個特定的對象,而不是一類對象。通過繼承對象來爲整個對象類添加功能是很容易的,但不可能通過這種方式擴展單個對象。使用裝飾器模式,您可以將功能添加到單個對象,並讓其他人無需修改即可。

在Java中,裝飾器模式的一個經典示例是Java I/O Streams實現。

FileReader  frdr = new FileReader(filename); 
LineNumberReader lrdr = new LineNumberReader(frdr); 

前面的代碼創建的讀取器 - lrdr - 即從一個文件,音軌行號讀取。第1行創建文件閱讀器(frdr),第2行添加行號跟蹤。

其實,我強烈建議您查看Java I/O類的Java源代碼。

7

裝飾器很簡單但非常強大。這是實現問題分離的關鍵,也是開放封閉原則的重要工具。以訂貨爲產品的一個常見的例子:

IOrderGateway 
{ 
    void PlaceOrder(Order order); 
{ 

主要實現:AmazonAffiliateOrderGateway

可能的裝飾可能是:

  • IncrementPerformanceCounterOrderGateway
  • QueueOrderForLaterOnTimeoutOrderGateway
  • EmailOnExceptionOrderGateway
  • InterceptTestOrderAndLogOrderGateway

有關更詳細的示例,請參閱here

2
  1. 可以動態和透明地爲單個對象添加責任。
  2. 責任可以撤回。
  3. 當通過子類擴展是不切實際的。有時可能會有大量的獨立擴展,並會產生一個子類的爆炸以支持每個組合。
7

Decorator模式在運行時動態更改對象的功能而不會影響對象的現有功能。

關鍵用例:

  1. 添加額外的功能/職責動態
  2. 刪除功能/職責動態
  3. 避免過多的子類添加額外的責任。

缺點:

  1. 打開過度使用封閉原則(對擴展開放和閉合進行修改)。在代碼最不可能改變的地方謹慎使用此功能。
  2. 太多的小類,並會增加維護開銷

現實世界的例子:計算飲料的價格,其中可能包含多種口味。

abstract class Beverage { 
    protected String name; 
    protected int price; 
    public Beverage(){ 

    } 
    public Beverage(String name){ 
     this.name = name; 
    } 
    public void setName(String name){ 
     this.name = name; 
    } 
    public String getName(){ 
     return name; 
    } 
    protected void setPrice(int price){ 
     this.price = price; 
    } 
    protected int getPrice(){ 
     return price; 
    } 
    protected abstract void decorateBeverage(); 

} 
class Tea extends Beverage{ 
    public Tea(String name){ 
     super(name); 
     setPrice(10); 
    } 
    public void decorateBeverage(){ 
     System.out.println("Cost of:"+ name +":"+ price); 
     // You can add some more functionality 
    } 
} 
class Coffee extends Beverage{ 
    public Coffee(String name){ 
     super(name); 
     setPrice(15); 
    } 
    public void decorateBeverage(){ 
     System.out.println("Cost of:"+ name +":"+ price); 
     // You can add some more functionality 
    } 
} 
abstract class BeverageDecorator extends Beverage { 
    protected Beverage beverage; 
    public BeverageDecorator(Beverage beverage){  
     this.beverage = beverage; 
     setName(beverage.getName()+"+"+getDecoratedName()); 
     setPrice(beverage.getPrice()+getIncrementPrice()); 
    } 
    public void decorateBeverage(){ 
     beverage.decorateBeverage(); 
     System.out.println("Cost of:"+getName()+":"+getPrice()); 
    } 
    public abstract int getIncrementPrice(); 
    public abstract String getDecoratedName(); 
} 
class SugarDecorator extends BeverageDecorator{ 
    public SugarDecorator(Beverage beverage){ 
     super(beverage); 
    } 
    public void decorateBeverage(){ 
     super.decorateBeverage(); 
     decorateSugar();   
    } 
    public void decorateSugar(){ 
     System.out.println("Added Sugar to:"+beverage.getName()); 
    } 
    public int getIncrementPrice(){ 
     return 5; 
    } 
    public String getDecoratedName(){ 
     return "Sugar"; 
    } 
} 
class LemonDecorator extends BeverageDecorator{ 
    public LemonDecorator(Beverage beverage){ 
     super(beverage); 
    } 
    public void decorateBeverage(){ 
     super.decorateBeverage(); 
     decorateLemon();  
    } 
    public void decorateLemon(){ 
     System.out.println("Added Lemon to:"+beverage.getName());  
    } 
    public int getIncrementPrice(){ 
     return 3; 
    } 
    public String getDecoratedName(){ 
     return "Lemon"; 
    } 
} 

public class VendingMachineDecorator { 
    public static void main(String args[]){ 
     Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea"))); 
     beverage.decorateBeverage(); 
     beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino"))); 
     beverage.decorateBeverage(); 
    } 
} 

輸出:

Cost of:Assam Tea:10 
Cost of:Assam Tea+Lemon:13 
Added Lemon to:Assam Tea 
Cost of:Assam Tea+Lemon+Sugar:18 
Added Sugar to:Assam Tea+Lemon 
Cost of:Cappuccino:15 
Cost of:Cappuccino+Lemon:18 
Added Lemon to:Cappuccino 
Cost of:Cappuccino+Lemon+Sugar:23 
Added Sugar to:Cappuccino+Lemon 

本例計算中增加了許多風味飲料後自動販賣機飲料的成本。

在上面的例子:

茶= 10,檸檬= 3和糖= 5的成本如果進行糖+檸檬+茶,它的成本18.

成本咖啡= 15,檸檬的= 3和Sugar = 5。如果您製作Sugar +檸檬+咖啡,則需花費23

通過對兩種飲料(茶和咖啡)使用相同的裝飾器,子類的數量已減少。在沒有Decorator模式的情況下,您應該爲不同的組合使用不同的子類。

的組合將是這樣的:

SugarLemonTea 
SugarTea 
LemonTea 

SugarLemonCapaccuino 
SugarCapaccuino 
LemonCapaccuino 

通過使用相同裝飾兩種飲料,子類的數量已經減少。這可能是由於組合而不是繼承在這種模式中使用的概念。

相關SE問題:

Decorator Pattern for IO

相關鏈接:

design-patterns-decorator通過dzone通過

decorator sourcemaking

oodesign文章