3

我有这段代码解释了装饰器模式:

 public abstract class IBeverage {
    protected string description = "Unknown beverage";

    public virtual string getDescription() {
        return description;
    }
}

public abstract class CondimentDecorator : IBeverage {
    public abstract string getDescription();
}

public class Espresso : IBeverage {
    public Espresso() {
        description = "Espresso";
    }
}

public class Mocha : CondimentDecorator {
    IBeverage beverage;

    public Mocha(IBeverage beverage) {
        this.beverage = beverage;
    }

    public override string getDescription() {
        return beverage.getDescription() + ", Mocha";
    }
}

我应该像这样使用它:

static void Main(string[] args) {
    IBeverage b = new Espresso();
    Console.WriteLine(b.getDescription());
    b = new Mocha(b);
    Console.WriteLine(b.getDescription());

    Console.ReadKey();
}

当我创建饮料 (Beverage b = new Espresso();) _description 更新为“Espresso”,当我用 Mocha (b = new Mocha(b)) 装饰 b 时,_description 将采用原始值“Unknown Beverage”。应该是“浓咖啡,摩卡”。怎么了?

这段代码最初是用 Java 编写的(本书是用 Java 编写的),但我将它翻译成 C#。我猜 Java 的工作方式与 C# 有点不同。

4

2 回答 2

4

因为GetDescription()不是virtual

public virtual string GetDescription() { ... }

virtual是 的伴随关键字override,它允许子类覆盖方法。这是 C# 与 Java 的关键区别。在 Java 中,所有方法都是隐式虚拟的。

于 2013-04-27T02:54:21.720 回答
3

您实际上在这里遇到了一些问题(可能与 Java 的设计不同)。即使在对所有命名问题进行​​排序之后,您也不会得到您期望的结果。

public abstract class CondimentDecorator : IBeverage {
    public abstract string GetDescription();
}
  • 该类CondimentDecorator实际上将隐藏IBeverage版本GetDescription()方法(从技术上讲,您应该使用public new abstract string GetDescription();.

  • 您通过将MochaIBeverage分配给b变量(您之前将其定义为 IBeverage via IBeverage b = new Espresso(),方法的IBeverage版本GetDescription()是实际触发的(完全忽略方法的Mocha覆盖CondimentDecorator GetDescription())将其分类为

如果您单步执行代码,您会看到这一点。尝试使用

CondimentDecorator m = new Mocha(b);
Console.WriteLine(m.GetDescription());

你会得到你所期望的。

然而,在我看来,这种方式违背了使用装饰器的目的。更好的选择是稍微改变设计并去掉 CondimentDecorator。除了混乱和意外行为之外,它没有提供任何东西。而是试试这个:

这是您唯一需要的抽象 Beverage 类:

public abstract class Beverage
{
    // c# convention is to use properties instead of public fields.
    // In this case I've used a private readonly backing field. 
    private readonly string _description = "Unknown Beverage";

    protected string Description
    {
        get { return _description; }
        set { _description = value; }
    }

    // Make this method virtual so you can override it, but if you
    // choose not to, this is the default behaviour.
    public virtual string GetDescription()
    {
        return Description;
    }
}

这是一个标准的饮料类(可以装饰):

public class Espresso : Beverage
{
    public Espresso()
    {
        // Setting the Beverage class Description property.
        // You can use base.Description if you prefer to be explicit
        Description = "Espresso";
    }
}

这是一个装饰另一个 Beverage 类的 Beverage 类:

public class Mocha : Beverage
{
    // store an instance of the Beverage class to be decorated
    private readonly Beverage _beverage;

    // Beverage instance to be decorated is passed in via constructor
    public Mocha(Beverage beverage)
    {
        _beverage = beverage;
    }

    // Override Beverage.GetDescription
    public override string GetDescription()
    {
        // Calls decorated Beverage's GetDescription and appends to it.
        return _beverage.GetDescription() + ", Mocha";
    }
}

现在要获得您期望的行为,您可以运行与上面相同的代码:

static void Main(string[] args) 
{
    Beverage b = new Espresso();
    Console.WriteLine(b.getDescription()); // "Espresso"
    b = new Mocha(b);
    Console.WriteLine(b.getDescription()); // "Espresso, Mocha"

    Console.ReadKey();
}

作为旁注。 Console.ReadKey(); 您可以在调试时 避免使用Ctrl+F5 这将自动"Press any key to continue..."为您输入。

更新

由于您想包含 CondimentDecorator 类(如您的评论中所述),您可以创建以下类:

public abstract class CondimentDecorator : Beverage
{
    private readonly Beverage _beverage;

    protected Beverage Bevy
    {
        get { return _beverage; }
    }

    protected CondimentDecorator(Beverage beverage)
    {
        _beverage = beverage;
    }
}

然后,您将Mocha类更改为以下内容:

// override CondimentDecorator instead of Beverage
public class Mocha : CondimentDecorator
{
    // Pass the Beverage to be decorated to the base constructor
    // (CondimentDecorator)
    public Mocha(Beverage beverage)
        : base(beverage)
    {
        // nothing needed in this constructor
    }

    public override string GetDescription()
    {
        // Now access the CondimentDecorator's Beverage property 
        // (which I called Bevy to differentiate it)
        return Bevy.GetDescription() + ", Mocha";
    }
}
于 2013-04-27T04:14:45.897 回答