何时使用装饰器模式?

Ale*_*sky 58 design-patterns decorator

我正在研究我的设计模式,我在编码中尚未认真使用的一种模式是装饰模式.

我理解这种模式,但我想知道的是现实世界中一些具体的例子,装饰者模式是最佳/最佳/优雅的解决方案.需要装饰器模式的特定情况非常方便.

谢谢.

Pas*_*ent 72

装饰器模式用于向特定对象添加附加功能,而不是一类对象.通过子类化对象很容易为整个对象类添加功能,但是不可能以这种方式扩展单个对象.使用Decorator Pattern,您可以向单个对象添加功能,并保留其他类似对象不受修改.

在Java中,装饰器模式的经典示例是Java I/O Streams实现.

FileReader       frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);
Run Code Online (Sandbox Code Playgroud)

上面的代码创建了一个读取器 - lrdr从文件中读取并跟踪行号.第1行创建文件阅读器(frdr),第2行添加行号跟踪.

实际上,我强烈建议您查看Java I/O类的Java源代码.


Cha*_*lts 36

装饰器模式与流使用很多:您可以使用流包装流以获得添加的功能.我已经在.Net框架中看到了这一点 - 据我所知,这发生在其他地方.我最喜欢的是在FileStream周围使用GZipStream来增加压缩.

  • 类似地,Java流只是装饰器的巨大集合,装饰装饰器装饰了装饰物.我的脸刚刚融化了. (16认同)
  • 我认为溪流会比我的Head First:Design Patterns书中的咖啡示例更好. (8认同)
  • 谁能解释为什么装饰器模式是这种情况的最佳解决方案? (2认同)

Kal*_*see 28

我最近在使用以下CommandProcessor接口的Web服务中使用了装饰器模式:

public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);
Run Code Online (Sandbox Code Playgroud)

基本上,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
   }

}
Run Code Online (Sandbox Code Playgroud)

因此真正的CommandProcessor包含在TimerDecorator中,我可以像处理目标CommandProcessor一样处理TimerDecorator,但现在添加了时序逻辑.


Rav*_*abu 8

装饰器模式在运行时动态更改对象的功能,而不会影响对象的现有功能.

关键用例:

  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();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

该实施例在向饮料中添加许多香料后计算自动售货机中的饮料成本.

在上面的例子中:

茶的成本= 10,柠檬= 3,糖= 5.如果你制作糖+柠檬+茶,它的成本为18.

咖啡的成本= 15,柠檬= 3,糖= 5.如果你制作糖+柠檬+咖啡,它的成本是23

通过对两种饮料(茶和咖啡)使用相同的装饰器,子类的数量已经减少.如果没有Decorator模式,您应该为不同的组合使用不同的子类.

组合将是这样的:

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino
Run Code Online (Sandbox Code Playgroud)

等等

通过对两种饮料使用相同的装饰器,子类的数量已经减少.由于此模式中使用的组合而非继承概念,这是可能的.

相关SE问题:

IO的装饰模式

有用的链接:

dzone的design-patterns-decorator

来源制造的装饰

oodesign文章


Mik*_*nty 7

装饰器很简单但非常强大.它是实现关注点分离的关键,也是开放封闭原则的重要工具.举一个订购产品的常见例子:

IOrderGateway
{
    void PlaceOrder(Order order);
{
Run Code Online (Sandbox Code Playgroud)

主要实施: AmazonAffiliateOrderGateway

可能的装饰者可能是:

  • IncrementPerformanceCounterOrderGateway
  • QueueOrderForLaterOnTimeoutOrderGateway
  • EmailOnExceptionOrderGateway
  • InterceptTestOrderAndLogOrderGateway

看看这里有一个更详细的例子.