我正在审查我的设计模式,而我尚未在我的编码中认真使用的一种模式是装饰者模式。
我理解这个模式,但我想知道一些现实世界中使用装饰者模式的好的具体例子,即需要装饰者模式作为最佳/最优/最优雅解决方案的特定情况。
谢谢。
我正在审查我的设计模式,而我尚未在我的编码中认真使用的一种模式是装饰者模式。
我理解这个模式,但我想知道一些现实世界中使用装饰者模式的好的具体例子,即需要装饰者模式作为最佳/最优/最优雅解决方案的特定情况。
谢谢。
装饰器模式
用于为现有的对象
(即在运行时已实例化的类)添加附加功能,而不是针对对象的类
和/或子类。通过继承一个对象的类,可以很容易地向整个类别的对象添加功能,但无法以这种方式扩展单个对象。通过使用装饰器模式,您可以为单个对象添加功能,并使其它相似的对象保持不变。
在Java中,装饰器模式的经典示例是Java I/O Streams的实现。
FileReader frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);
上面的代码创建了一个读取器 -- lrdr
,它从文件中读取并跟踪行号。第1行创建了一个文件读取器 (frdr
),第2行增加了行号跟踪。
实际上,我强烈建议您查看Java I/O类的Java源代码。
装饰器模式在流技术中被广泛应用:您可以使用一个流来包装另一个流,以获得增加的功能。就我所知,这在 .Net 框架中常见,其他地方也可能存在。我最喜欢的是将 GZipStream 用于 FileStream,以实现额外的压缩。
最近我在一个使用CommandProcessor接口的Web服务中使用了装饰器模式:
public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);
基本上,CommandProcessor接收一个请求,并创建适当的命令,执行该命令并创建适当的响应,并发送响应。 当我想要添加计时和记录日志时,我创建了一个TimerDecorator,它使用现有的CommandProcessor作为其组件。 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,但现在计时逻辑已经添加了。
装饰器模式可以在运行时动态地改变对象的功能。
组合和继承已经被有效地使用来实现这个目标。
一个真实的例子:计算饮料的价格,其中可能包含多种口味。
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。
通过为两种饮料(茶和咖啡)使用相同的装饰器,减少了子类的数量。如果没有装饰器模式,您应该为不同的组合创建不同的子类。
组合将如下所示:
SugarLemonTea
SugarTea
LemonTea
SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino
等等。
通过为两种饮料使用相同的装饰器,子类的数量已经减少。
相关SE问题:
IOrderGateway
{
void PlaceOrder(Order order);
{
主要实现: AmazonAffiliateOrderGateway
可能的装饰器包括:
IncrementPerformanceCounterOrderGateway
(增加性能计数器订单网关)QueueOrderForLaterOnTimeoutOrderGateway
(将订单排队以便在超时后处理订单网关)EmailOnExceptionOrderGateway
(在异常情况下发送电子邮件订单网关)InterceptTestOrderAndLogOrderGateway
(拦截测试订单并记录订单网关)从此处更详细的例子说明了一个装饰器,用于在完成订单时保存使用礼品卡创建的订单的联系人:
class OrderWithGiftCardGateway extends OrderGatewayDecorator
{
...
public function createOrder(CreateOrderRequest $order)
{
if ($this->containsGiftCard($order))
{
$this->addContactToFolder($order);
}
return parent::createOrder($order);
}
}
GOF定义:
动态地将附加职责添加到对象。装饰器为扩展功能提供了一种灵活的替代子类化的方式。
该模式表明类必须对修改关闭,但对扩展开放,可以添加新功能而不会干扰现有功能。当我们想要为特定对象添加特殊功能而不是整个类时,这个概念非常有用。在此模式中,我们使用对象组合的概念而不是继承。
通用示例:
public abstract class Decorator<T> {
private T t;
public void setTheKernel(Supplier<? extends T> supplier) {
this.t = supplier.get();
}
public T decorate() {
return Objects.isNull(t) ? null : this.t;
}
}
public interface Repository {
void save();
}
public class RepositoryImpl implements Repository{
@Override
public void save() {
System.out.println("saved successfully");
}
}
public class EnhancedRepository<T> extends Decorator<T> {
public void enhancedSave() {
System.out.println("enhanced save activated");
}
}
public class Main {
public static void main(String[] args) {
EnhancedRepository<Repository> enhanced = new EnhancedRepository<>();
enhanced.setTheKernel(RepositoryImpl::new);
enhanced.enhancedSave();
enhanced.decorate().save();
}
}
装饰器模式被 C# 语言本身使用。它用于装饰 C# 的 Stream I/O 类。 装饰版本包括 BufferedStream、FileStrem、MemoryStrem、NetworkStream 和 CryptoStream 等类。
这些子类继承自 Stream 类,并且还包含一个 Stream 类的实例。
在这里阅读更多
当您想要为类/对象添加多个功能,并且希望随时具有添加它们的灵活性时,装饰者模式非常方便。您可以扩展基类并添加所需的更改,但是这种方式会产生许多可能会让您头脑爆炸的子类。但是,通过使用装饰者模式,您可以拥有所需的更改,同时仍然具有简单、易懂的流程。 您的设计很容易进行扩展,但对于修改则非常简单。 最好的例子可能是 Java 和 C# 中实现的 Stream 对象。 例如,您有一个 File Stream,在一个用例中,您想要对其进行加密,然后压缩,然后记录日志,最后做一些花哨的操作。然后在另一个类中,您决定执行其他操作:您想要将其转换,然后加密,然后获取时间等。如果使用继承,则必须创建至少 3 个子类,如果需要任何其他要求,则必须添加更多子类,在这种情况下(Stream),您将获得数十个子类以进行小的更改。
class EncryptZipLogStream{}
class ConvertEncryptTimeStream{}
class MoreStreamProcess{}
class OtherMoreStreamProcess{}
...
class WhereIWannaUseUseCaseOne {
EncryptStream(ZipStream(LogStream(FileStream("file name)))));
// rest of the code to use the combines streams.
}
然后你想出了另一个使用案例:
class WhereIWannaUseUseCaseSecond {
ConvertStream(TimeStream(LogStream(FileStream("file name)))));
// rest of the code to use the combines streams.
}
等等,您可以在运行时使用简单的流程和易于理解的逻辑灵活地进行任何操作。
一个对我来说非常真实的例子:
我不得不更新在项目中广泛使用的类,但是那个类在一个库中,源代码丢失在虚无世界中。
我可以将整个库反编译以创建另一个版本,或者使用装饰器设计模式,我选择了后者。这使我能够添加缺少的功能并简单地更新配置。
当负责任地使用时,这是一种非常有用的模式。
这个特殊案例启发了我制作 this video,在视频中我解释了这个模式。