这是一个装饰器模式还是策略模式,还是两者都不是?

8

I have the following interface.

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();
    }
}

5
为什么PowerSwitchDecorator没有实现PowerSwitch接口?它确实可以这样做... - Ani
7个回答

7
目前,任何想使用toggleOnOff(int)toggleOnOff()方法的代码都需要一个 PowerSwitchDecorator实例,而不是PowerSwitch。这有点违背了装饰器应该对客户端透明的初衷。
如果您希望所有实现都拥有这些方法,应在 PowerSwitch接口中包含它们。
然后,如@Ani所建议的那样,您可以修改上面的PowerSwitchDecorator,使其扩展PowerSwitch,以便可以进行以下操作:
PowerSwitch switch = new PowerSwitchDecorator(new ConcretePowerSwitch());
switch.toggleOnOff();

现在你有一个类型为 PowerSwitch 的变量,具有 PowerSwitchDecorator 的功能。 编辑:请注意,如果已经符合您的需求,则应仅使用已建立的模式。如果您的方法适用于您,请使用您已展示的方法。无需将其强行塞入特定模式中。
你想传递哪种类型的对象?你希望在你的 API 中有这样的方法吗:
void connect(PowerSwitch powerSwitch, Appliance appliance);

或者像这样的方法:
void connect(PowerSwitchDecorator powerSwitch, Appliance appliance);

抱歉,下面的例子不是很好

如果你想要前者,那么每个人都必须手动为他们的 PowerSwitch添加装饰才能获得几个方便的方法。现在对你来说可能很方便,但我认为这对于你代码的用户来说可能会很不方便,他们可能不会费心去做这件事。如果你想要后者,你需要在你的方法签名中使用类型 PowerSwitchDecorator,这意味着你总是处理 PowerSwitchDecorator 而不是原始的 PowerSwitch


1
但这是否意味着我需要在所有powerswitch实现中实现虚拟方法(toggleOnOff)?此外,如果我想在Decorator中稍后添加新方法(比如toggleOffOn),那么我需要在PowerSwitch接口中添加它并更新所有当前的PowerSwitch实现(因为它们将无法编译了)?也可能是我无法访问所有powerSwitch实现(可能是服务提供者接口的一部分)。 - etxalpo
1
作为etxalpo,我不同意。仅仅通过让装饰器实现被装饰接口,它仍然是有用的,因为所有PowerSwitch的客户端都可以使用PowerSwitchDecorator。就像所有Reader的客户端都可以透明地使用BufferedReader一样。这并不妨碍BufferedReader添加自己的方法(例如readLine()),使得该模式非常有用。 - JB Nizet
没错。你可能想要一个抽象类,其中包含所有实现都继承的基本切换方法。然后,如果你想要特定的切换方式,可以进行装饰。我编辑了我的答案,希望能有所帮助 ;) - Gyan aka Gary Buyn
@JB 很好,完全正确。我猜这取决于预期的使用方式。 - Gyan aka Gary Buyn
@JB 我可能在回答中过于依赖他提到的想要在所有实现中都使用,并且这只是为了方便。 - Gyan aka Gary Buyn
我喜欢抽象类的解决方案。我会看一下。谢谢 :) - etxalpo

3
这是一个很好的做法,如果您需要在运行时增强PowerSwitch实例。我建议您在装饰器中实现PowerSwitch接口。装饰器模式的另一个名称是代理模式。
您可以在扩展了PowerSwitch接口的另一个接口中定义扩展方法。这将避免在需要调用这些扩展方法时依赖于装饰器。
当您需要在编译时增强或重新定义某个行为时,扩展类是一个很好的做法。

谢谢!我会让装饰器实现PowerSwitch接口。 - etxalpo

0

这不是两种模式之一。

装饰器的目的是包装一个对象并扩展已有的功能,使接口对客户端透明。BufferedInputstream就是一个例子。请注意,装饰器应该实现与您要包装的类型相同的接口。

//Let Type be the interface that both the Decorator and DecoratedClass implements
Type yourInstance = new Decorator(new DecoratedClass());

请注意与代理模式的区别,代理模式的主要目的是控制对对象的访问,不一定通过包装另一个对象来实现。在这种情况下,您还可以让代理实现相同的接口。

当你说“Not that…”和“Not the difference…”时,你是指“注意…”和“注意区别…”吗? - JB Nizet
是的,抱歉打错了。谢谢! - Thomas Johan Eggum

0

我会说这不是任何一种模式。

装饰器模式

装饰器模式用于扩展现有实现的行为。以图形窗口为例,您可能希望拥有一个带有滚动条的窗口。然后您可以创建一个类似于:

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();

       ...
    }
}

请阅读以下链接获取更多信息:

0

你的 PowerSwitchDecorator 类不是装饰器。你的实现接近于 策略模式,尽管命令类似于 命令模式

装饰器 模式中,装饰器 实际上实现了接口(即 Component),而你的类没有这样做。

看一下类图:

enter image description here

在上面的图表中,Component 是一个接口。 Decorator 实现了 Component 接口,并包含具有 Composition 的接口。请查看成员变量 - component
为了更好地理解,请参考以下问题:

IO 的装饰器模式

策略模式的实际例子


0

如果装饰器还实现了PowerSwitch接口,那就真的是一个装饰器了。我会将其归类为对象聚合。


0

有些模式看起来很相似,它们的区别在于使用意图。关于这个主题,这里有一个更通用的讨论:何时以及如何使用策略模式代替装饰器模式?

根据你的代码,目前没有真正的区别。它看起来像是一种策略,因为PowerSwitchDecorator只是将工作(即算法)委托给了PowerSwitch。如果这是你的意图,并且有不同方式切换的备选PowerSwitches,则策略模式是你的选择。

如果你有一组PowerSwitches,每个开关都稍微增强了切换功能(装饰切换),并且这些PowerSwitches可以嵌套,则你正在实现一个装饰器。正如之前所说,在这种情况下,你还需要让装饰器成为类型层次结构的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接