这是装饰器模式还是策略模式,或者两者都不是?
Posted
技术标签:
【中文标题】这是装饰器模式还是策略模式,或者两者都不是?【英文标题】:Is this a decorator or a strategy pattern, or neither of the two? 【发布时间】:2012-02-17 12:27:20 【问题描述】:我有以下界面。
PowerSwitch.java
public interface PowerSwitch
public boolean powerOn();
public boolean powerOff();
public boolean isPowerOn();
上述接口应包含可以派生任何其他功能的最少方法集,以便尽可能轻松地添加其他 PowerSwitch 实现。
我想在运行时向 PowerSwitch 接口添加功能(装饰器所做的),方法是创建一个包含 PowerSwitch 实例的组合并添加新方法的类,例如两个 toggleOnOff() 方法如下。这样我只需要实现两个切换方法一次,它将适用于所有 PowerSwitch 实现。
这被认为是一种好/坏的做法吗?如果不好,还有其他建议吗?
它并不真正符合装饰器模式,因为它添加了额外的方法。它是一种策略模式,还是一种组合模式?还是它有另一个模式名称?有没有“界面装饰器”之类的东西?
PowerSwitchDecorator.java
public class PowerSwitchDecorator
private PowerSwitch ps;
public PowerSwitchDecorator(PowerSwitch ps)
this.ps = ps;
public void toggleOnOff(int millis) throws InterruptedException
powerOn();
Thread.sleep(millis);
powerOff();
public void toggleOnOff()
powerOn();
powerOff();
public boolean powerOn()
return ps.powerOn();
public boolean powerOff()
return ps.powerOff();
public boolean isPowerOn()
return ps.isPowerOn();
【问题讨论】:
为什么PowerSwitchDecorator不实现PowerSwitch接口?它当然可以... 【参考方案1】:您的 PowerSwitchDecorator
类不是装饰器。即使命令类似于Command_pattern,您的实现也接近Strategy_pattern
在Decorator
模式中,Decorator
实际上实现了接口(即Component
),而您的类并没有这样做。
看看类图
在上图中,Component
是一个接口。 Decorator
实现了Component
接口并包含Composition
的接口。查看成员变量 - component
。
请参阅以下问题以更好地理解:
Decorator Pattern for IO
Real World Example of the Strategy Pattern
【讨论】:
【参考方案2】:事实上,任何想要使用toggleOnOff(int)
或toggleOnOff()
方法的代码都需要PowerSwitchDecorator
的实例,而不是PowerSwitch
。这种方式违背了装饰器应该对客户端透明的目的。
如果您希望所有实现都具有这些方法,则应将它们包含在PowerSwitch
接口中。
然后,正如@Ani 建议的那样,您可以修改上面的PowerSwitchDecorator
以扩展PowerSwitch
,这样您就可以这样做:
PowerSwitch switch = new PowerSwitchDecorator(new ConcretePowerSwitch());
switch.toggleOnOff();
现在您有了一个具有PowerSwitchDecorator
功能的PowerSwitch
类型的变量。
编辑:请注意,只有当它满足您的需要时,您才应该使用已建立的模式。如果它适合您,您可以使用您展示的方法。无需将其硬塞到特定的模式中。
你想传递什么类型的对象?你想在你的 API 中使用这样的方法吗:
void connect(PowerSwitch powerSwitch, Appliance appliance);
或者像这样的方法:
void connect(PowerSwitchDecorator powerSwitch, Appliance appliance);
(对不起,它们不是很好的例子)
如果您想要前者,那么每个人都将不得不手动“装饰”他们的PowerSwitch
以获得一些方便的方法。现在对你来说可能很方便,但我认为这对你的代码的用户来说会很不方便,他们可能不会为此烦恼。如果你想要后者,你必须在你的方法签名中使用PowerSwitchDecorator
类型,这往往意味着你总是处理PowerSwitchDecorator
s而不是原始PowerSwitch
es。
【讨论】:
但这是否意味着我需要在所有电源开关实现中实现虚拟方法(toggleOnOff)?另外,如果我想稍后在装饰器中添加一个新方法(比如说 toggleOffOn),那么我需要在 PowerSwitch 接口中添加它并更新所有当前的 PowerSwitch 实现(因为它们将不再编译)?也可能是我无法访问所有 powerSwitch 实现的情况(可能是服务提供者接口的一部分)。 作为 etxalpo,我不同意。只要让装饰器实现装饰接口,它仍然很有用,因为 PowerSwitch 的所有客户端都可以使用 PowerSwitchDecorator。就像 Reader 的所有客户端都可以透明地使用 BufferedReader 一样。这不会阻止 BufferedReader 添加自己的方法(例如 readLine()),并且这种模式非常有用。 是的,没错。您可能需要一个具有基本切换方法的抽象类,您的所有实现都从该类继承。然后,如果你想要一种特定的切换方式,你可以装饰它。我编辑了我的答案,希望它有所帮助;) @JB 够公平的。这是完全正确的。我想这取决于预期的用途。 @JB 我的回答可能过于基于他提到的在所有实现中都需要它,而这只是为了方便。【参考方案3】:我会说这不是模式。
装饰器模式
装饰器模式用于扩展现有实现的行为。以图形窗口为例,您可能希望有一个带有滚动条的窗口。然后你可以有一个类似的课程
public class ScrollBarsWindow : Window
private Window windowToDecorate;
public ScrollBarsWindow(Window windowToDecorate)
this.windowToDecorate = windowToDecorate;
public void Draw()
windowToDecorate.Draw();
DrawScrollBars();
public void DrawScrollBars()
Draw the scroll bars
策略模式
策略模式用于根据所选策略执行不同的操作。假设您正在煮咖啡。你可以有类似的东西:
public interface IMakeCoffeeStrategy
public Coffee MakeCoffee();
public class CappuccinoStrategy : IMakeCoffeeStrategy
public Coffee MakeCoffee make your cappuccion
public class LatteStrategy : IMakeCoffeeStrategy
public Coffee MakeCoffee make your latte
public class Context
private IMakeCoffeeStrategy strategy;
public Context(IMakeCoffeeStrategy strategy)
this.strategy = strategy;
public Coffee MakeSomeCoffee()
return strategy.MakeCoffee();
并像使用它
public class MyCoffeeMachine
public Coffee MakeCoffee(CoffeeType coffeeType)
if(coffeeType == CoffeeType.Latte)
return new Context(new LatteStrategy()).MakeSomeCoffee();
else if(coffeeType == CoffeeType.Cappuccino)
return new Context(new CappuccinoStrategy()).MakeSomeCoffee();
...
阅读以下链接了解更多信息:
Strategy Decorator【讨论】:
【参考方案4】:几种模式通常看起来相似,不同之处在于它们的使用意图。这是关于这个主题的更一般的线程:When and How Strategy pattern can be applied instead of decorator pattern?
使用您的代码,还没有真正的区别。它看起来像是一种策略,因为 PowerSwitchDecorator 只是将工作(即算法)委托给 PowerSwitch。如果这是您的意图,并且有以不同方式切换的备用电源开关,则策略模式是您的选择。
如果您有一组 PowerSwitch,每个都以轻微的方式增强切换(装饰切换)并且这些 PowerSwitch 可以嵌套,那么您正在实现一个装饰器。如前所述,在这种情况下,您还需要装饰器成为类型层次结构的一部分。
【讨论】:
【参考方案5】:这两种模式都不是。
装饰器的目的是包装一个对象并扩展现有的功能,使接口对客户端透明。 BufferedInputstream 就是一个例子。请注意,装饰器应该实现与您要包装的类型相同的接口。
//Let Type be the interface that both the Decorator and DecoratedClass implements
Type yourInstance = new Decorator(new DecoratedClass());
注意与代理模式的区别,代理模式的主要目的是控制对对象的访问,不一定通过包装另一个对象。在这种情况下,您还可以让代理实现相同的接口。
【讨论】:
当您说“不是那个...”和“没有区别...”时,您的意思是“请注意...”和“请注意区别...”吗? 是的,抱歉打错了。谢谢!【参考方案6】:如果装饰器也实现了 PowerSwitch 接口,那它就真的是一个装饰器了。我将其描述为对象聚合。
【讨论】:
【参考方案7】:如果您需要在运行时增强 PowerSwitch 实例,这是一个很好的做法。我建议你在装饰器中实现 PowerSwitch 接口。装饰器模式的另一个名称是代理模式。
您可以在扩展您的 PowerSwitch 接口的另一个接口中定义扩展方法。当需要调用这些扩展方法时,这将避免对装饰器的依赖。
当您需要在编译时增强或重新定义行为时,扩展类是一种很好的做法。
【讨论】:
谢谢!我会让装饰器实现 PowerSwitch 接口以上是关于这是装饰器模式还是策略模式,或者两者都不是?的主要内容,如果未能解决你的问题,请参考以下文章