装饰器模式与继承的例子

Jon*_*Jon 31 .net c# inheritance design-patterns decorator

我一直在尝试使用装饰器模式来扩展你不想触摸的代码的功能,我看到了如何实现它,但我现在不确定你为什么不继承原始类并扩展那种方式.

我已经读过装饰器模式允许您在运行时添加功能,而继承意味着它在编译时.

我不明白这一点.

有人可以解释一下,提供示例并解释何时使用装饰器与继承更好.

谢谢

Raz*_*zvi 32

假设您创建了一个以特定方式显示项目的View类.现在您决定还需要一个可滚动的版本,因此您创建了一个继承View的ScrollableView.稍后您决定还需要带边框的版本,因此您现在需要创建BorderedView和BorderdScrollableView.

另一方面,如果您可以为每个添加的样式制作装饰器.您将拥有以下课程:

  • 视图
  • ScrollableDecorator
  • BorderedDecorator

如果需要带边框的滚动视图,可以执行以下操作:

new BorderedDecorator(new ScrollableDecorator(new View())).

所以你可以配置这3个类的任意组合.你可以在运行时添加或删除它们(假设你单击一个添加边框的按钮,你现在用BorderDecorator包装你的视图......你需要实现这个视图类,如果你还没有,或者你需要创建一个新的视图实例并将所有相关数据从第一个视图复制到第二个视图,这不像添加或删除包装器那样容易.

  • 当然你也可以做类似的事情,甚至可能更有意义.但是,想象一下,如果您无法更改该类以添加这些属性,因为它不是您的源代码,而是通过虚拟方法等提供扩展点.或者,也许您希望保持基础对象相当普通,因为其他东西需要使用它的更通用的版本.但是我不会仔细阅读所提供的示例,为简单起见,大多数示例都是相当人为的. (3认同)
  • 我有同样的问题.为什么我们不将两个属性`Scrollable`和`Bordered`添加到`View`类(或者它继承的类)? (2认同)

Maj*_*ons 28

想象一下像"文明"这样的游戏,地图上的每个方格都可以附加各种资源(例如,各种矿石,木材或石油等).

如果使用直接继承,则需要为每种方块创建一个类.拥有它是笨拙的

public class OilSquare {}
public class OilAndGoldSquare {}
public class GoldAndSilverSquare {}
// etc.
Run Code Online (Sandbox Code Playgroud)

装饰器模式允许混合和匹配,而无需创建严格的层次结构.所以,你有:

public class Square {}
public class GoldDec {}
public class SilverDec {}
public class OilDec {}

// ...

var crazyMix = new GoldDec(new SilverDec(new OilDec(new Square())));
Run Code Online (Sandbox Code Playgroud)

换句话说,装饰器允许创建管道行为,管道中的每个步骤都可以与另一个步骤交换.


Chr*_*ris 13

正如其他人已经说装饰器有利于为事物添加"选项"......好处来自于你可以通过装饰器链接方法等.

想象一下,我买的车有皮革内饰,金属漆和令人敬畏的扰流板......

这三个选项有8种不同的组合,但是装饰器只需要三个额外的类.

有趣的是装饰图案的工作方式.作为一个简短的例子:

public class MetallicPaint : Car
{
    private Car car;
    public MetallicPaint(Car wrappedCar)
    {
        car = wrappedCar;
    }

    public decimal Cost()
    {
        return car.Cost() + 500;
    }

    public string Description()
    {
        return car.Description() + ", Metallic Paint";
    }
    public string Speed()
    {
        return car.Speed();
    }
    [... {pass through other methods and properties to the car object}]
}
Run Code Online (Sandbox Code Playgroud)

这不是一个完整的例子,但强调了装饰者如何与正在装饰的对象进行交互.当然,因为它实现了汽车,所以它可以像其他任何方式一样使用汽车(并且通过任何装饰器不会对内部汽车对象产生影响).

当然,如果你有多个这些装饰器,其中一个嵌套在里面的汽车将反过来增加他们的成本,他们的部分描述和扰流板将改变速度,而其他人没有...

从本质上讲,它允许您以比继承更加模块化和不太基本的方式修改对象.装饰器应该总是像它们是基础对象一样使用(在这种情况下Car),因此它们不应该暴露任何新方法或属性,只需稍微改变现有方法或属性的效果.


qar*_*tal 6

如果要添加许多功能并且还需要组合这些功能,装饰器模式优于继承.假设您的基类是A,并且您希望使用特征f1,f2,f3,f4以及它们的某些组合(如(f1,f2)和(f1,f3)和...)扩展(修饰)此基类.因此,您需要在层次结构中创建4!= 4*3*2*1 = 24类(每个功能4个,其余为其组合).而使用装饰图案,你只需要创建4个类!


对于@Reyvi中的@Seyed Morteza Mousavi帖子:你是对的,我们可以向View类添加两个属性Scrollable和Bordered,然后检查属性是否设置为true,以便运行所需的行为.但这需要我们已经知道我们需要的功能的数量(在装饰器模式中不是这种情况).否则,对于我们想要添加到我们的类的每个新特性(比如f1),我们需要改变我们的主类,或者继承主类(你会说)并添加属性.采用后一种方法,你还需要改变处理特征组合的代码部分(这不好,因为它不符合"松散耦合!"的经验法则)

希望这可以帮助.