何时使用装饰模式?
我正在检查我的devise模式,而我在编码中尚未认真使用的一种模式是Decorator模式。
我理解这个模式,但是我想知道的是在现实世界中,时代的一些很好的具体例子,装饰模式是最好的/最优的/优雅的解决scheme。 特定的情况下,需要装饰模式是非常方便的。
谢谢。
装饰器模式在stream中使用很多:你可以用一个stream包装一个stream来获得附加的function。 我已经看到.Net框架 – 据我所知这发生在其他地方。 我最喜欢在FileStream中使用GZipStream,以增加压缩。
Decorator模式用于向特定对象添加附加function,而不是一类对象。 通过inheritance一个对象来为整个对象添加function是很容易的,但是不可能用这种方式来扩展一个对象。 使用装饰模式,您可以将function添加到单个对象,并保持其他人不变。
在Java中,装饰器模式的一个经典示例是Java I / O Streams实现。
FileReader frdr = new FileReader(filename); LineNumberReader lrdr = new LineNumberReader(frdr);
上面的代码创build一个读取器 – lrdr
– 从文件读取并跟踪行号。 第1行创build一个文件阅读器( frdr
),第2行添加行号跟踪。
实际上,我强烈build议您查看Java I / O类的Java源代码。
我最近在使用以下CommandProcessor接口的Web服务中使用了装饰器模式:
public Command receive(Request request); public Response execute(Command command); public void respond(Response response);
基本上,CommandProcessor接收请求并创build适当的Command,执行Command并创build相应的Response,并发送Response。 当我想添加计时并logging它时,我创build了一个使用现有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中,我可以像处理目标CommandProcessor一样处理TimerDecorator,但是现在已经添加了时序逻辑。
Decorator模式在运行时dynamic地改变对象的function,而不会影响对象的现有function。
关键用例:
- dynamic添加其他function/职责
- dynamic删除function/职责
- 避免过多的子分类来增加额外的责任。
缺点:
- 过度使用开放式封闭原则 (Open for extension and Closed for modification)。 在代码最不可能更改的地方谨慎使用此function。
- 太多的小类,会增加维护的开销 。
一个真实世界的例子:计算饮料的价格,可能包含多种口味。
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,糖= 5。如果你做糖+柠檬+咖啡,它的成本是23
通过对两种饮料(茶和咖啡)使用相同的装饰器,减less了子类的数量。 在没有Decorator模式的情况下,您应该为不同的组合使用不同的子类。
组合将是这样的:
SugarLemonTea SugarTea LemonTea SugarLemonCapaccuino SugarCapaccuino LemonCapaccuino
等等
通过对两种饮料使用相同的装饰器 ,减less了子类的数量。 这可能是由于这种模式中使用的组合而不是inheritance概念。
相关的SE问题:
IO的装饰模式
有用的链接:
由dzone devise模式装饰
由sourcemaking的装饰者
oodesign文章
装饰器很简单,但function非常强大。 实现问题分离是关键,是开放封闭原则的重要工具。 举一个常见的例子来下订单:
IOrderGateway { void PlaceOrder(Order order); {
主要实现: AmazonAffiliateOrderGateway
可能的装饰可能是:
-
IncrementPerformanceCounterOrderGateway
-
QueueOrderForLaterOnTimeoutOrderGateway
-
EmailOnExceptionOrderGateway
-
InterceptTestOrderAndLogOrderGateway
在这里看一个更详细的例子。
Zend Framework使用表单元素的装饰器
更多信息: http : //framework.zend.com/manual/en/zend.form.decorators.html
- dynamic和透明地为单个对象添加责任。
- 可以撤回的责任。
- 当通过子类扩展是不切实际的。 有时可能会有大量的独立扩展,并且会产生一个子类的爆炸以支持每个组合。