C# Decorator模式与继承(附示例)

C# Decorator模式与继承(附示例),c#,.net,design-patterns,inheritance,decorator,C#,.net,Design Patterns,Inheritance,Decorator,我一直在试验decorator模式来扩展您不想接触的代码的功能,例如,我看到了如何实现它,但是我现在不确定您为什么不从原始类继承并以这种方式扩展 我已经读到decorator模式允许您在运行时添加功能,而继承意味着在编译时添加功能 我不明白 有人能解释一下吗?提供一些例子,解释一下什么时候使用decorator比使用继承更好 谢谢想象一个游戏式的文明,地图上的每个广场都可以有各种各样的资源(比如各种矿石、木材或石油等) 如果使用直接继承,则需要为每种正方形创建一个类。如果你有 public cl

我一直在试验decorator模式来扩展您不想接触的代码的功能,例如,我看到了如何实现它,但是我现在不确定您为什么不从原始类继承并以这种方式扩展

我已经读到decorator模式允许您在运行时添加功能,而继承意味着在编译时添加功能

我不明白

有人能解释一下吗?提供一些例子,解释一下什么时候使用decorator比使用继承更好


谢谢

想象一个游戏式的文明,地图上的每个广场都可以有各种各样的资源(比如各种矿石、木材或石油等)

如果使用直接继承,则需要为每种正方形创建一个类。如果你有

public class OilSquare {}
public class OilAndGoldSquare {}
public class GoldAndSilverSquare {}
// etc.
装饰器模式允许混合和匹配,而无需创建严格的层次结构。因此,你应该:

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

// ...

var crazyMix = new GoldDec(new SilverDec(new OilDec(new Square())));

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

假设您创建了一个视图类,以某种方式显示项目。 现在,您决定还需要一个可滚动的版本,因此您创建了一个继承该视图的可滚动视图。 稍后,您决定还需要一个带有边框的版本,因此现在需要创建BorderedView和BorderdScrollableView

如果在另一方面,你可以为每一个添加的样式做一个装饰。您将拥有以下课程:

  • 看法
  • 滚动装饰器
  • 边界去氧器
如果需要带边框的滚动视图,请执行以下操作:

newbordereddecorator(newscrollabledecorator(newview()))


因此,您可以使用这3个类配置任意组合。您可以在运行时添加或删除它们(假设您单击一个显示“添加边框”的按钮,您现在用一个BorderDecorator包装您的视图……如果您还没有实现继承,那么您需要实现这个视图类,或者您需要创建一个新的视图实例,并将所有相关数据从第一个视图复制到第二个视图,这不像添加或删除视图那么简单。)正如其他人已经说过的那样,decorator有助于为事物添加“选项”…好处在于可以通过decorator链接方法等

想象一下,我买了一辆有真皮内饰、金属漆和很棒的扰流板的车

这三个选项有8种不同的组合,但对于decorator,您只需要三个额外的类

有趣的是装饰图案的工作方式。举个简单的例子:

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}]
}
这不是一个完整的示例,但强调了装饰器如何与它所装饰的对象交互。当然,因为它实现了汽车,所以它可以像汽车一样在其他任何方面使用(并通过装饰器对内部汽车对象没有影响的任何内容)

当然,如果你有多个这样的装潢师,每个装潢师里面都嵌有一辆车,那么他们的成本、他们描述的部分以及扰流板可能会改变速度,而其他装潢师则不会


本质上,它允许您以比继承更模块化、更基本的方式修改对象。应该始终将装饰器当作基本对象使用(在本例中为
Car
)因此,它们永远不应该公开任何新方法或属性,只需稍微更改现有方法或属性的效果。

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


对于@Razvi post中的@Seyed Morteza Mousavi: 您是对的,我们可以向View类添加两个可滚动和带边框的属性,然后检查属性是否设置为true,以便运行所需的行为。但这要求我们已经知道所需功能的数量(在装饰器模式中不是这样)。否则,对于每个新功能(例如f1)我们想要添加到我们的类中,我们需要更改我们的主类,或者继承主类(您会说)并添加属性。采用后一种方法,您需要进一步更改处理功能组合的代码部分(这不好,因为它不遵守“松耦合”的经验法则)


希望这能有所帮助。

在继承和扩展方法中,都是在编译时添加函数。理解这一点可能是一个很好的起点,这篇维基百科文章专门介绍了Decorator模式:@peer请在回答中解释更多。我不明白Decorator是如何允许您在编译时做任何事情的runtime@Jon当前位置我添加了一些示例这是一个更实际的答案,你如何使用decorator可能会有所帮助。我不认为真的存在运行时/编译时分割,我会这样描述,但你可以选择应用于对象的decorator(例如用户选择)然后,该对象在运行时会表现出不同的行为,编译器不知道它是原始类型以外的任何东西。但我不会说它在运行时添加了功能性。:)同样,通过继承,您可以创建大量重复的代码。是的,我认为在这种情况下,很明显,使用指数类编号的继承是个坏主意,特别是因为正如你所说的,有很多重复。我想把更多的精力放在装饰的积极方面,而不是消极方面