我有这个解释装饰器模式的代码:
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 =新摩卡(b))装饰b时,_description采用原始值“Unknown”饮料”。它应该是“Espresso,Mocha”。 出了什么问题?
这段代码最初是用Java编写的(这本书是用Java编写的),但我把它翻译成了C#。我猜Java与C#有点不同。
答案 0 :(得分:4)
因为GetDescription()
不是virtual
。
public virtual string GetDescription() { ... }
virtual
是override
的伴随关键字,它允许子类覆盖方法。这是C#与Java的关键区别。在Java中,所有方法都是隐式虚拟的。
答案 1 :(得分:3)
你实际上在这里遇到了一些问题(也许是与Java不同的设计)。即使在对所有命名问题进行排序之后,您也无法获得预期的结果。
public abstract class CondimentDecorator : IBeverage {
public abstract string GetDescription();
}
CondimentDecorator
类实际上会隐藏IBeverage
版本GetDescription()
方法(技术上应该使用public new abstract string GetDescription();
。
您正在将Mocha
类分类为IBeverage
,并将其分配给b
变量(您之前通过IBeverage b = new Espresso()
将其定义为IBeverage, IBeverage
方法的GetDescription()
版本实际触发(完全忽略了Mocha
CondimentDecorator
方法的GetDescription()
覆盖)
如果您单步执行代码,则可以看到此内容。尝试使用
CondimentDecorator m = new Mocha(b);
Console.WriteLine(m.GetDescription());
你会得到你期望的。
然而,在我看来,这种方式违背了使用装饰器的目的。更好的选择是稍微改变设计并摆脱CondimentDecorator。除了混乱和意外行为之外,它不提供任何其他内容。相反,试试这个:
这是您唯一需要的抽象饮料课程:
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();
}
作为旁注。使用 Ctrl + F5 进行调试时,可以避免使用 Console.ReadKey();
这将自动放入{ {1}}为你。
更新
由于您想要包含CondimentDecorator类(如评论中所述),您可以创建以下类:
"Press any key to continue..."
然后,您可以将public abstract class CondimentDecorator : Beverage
{
private readonly Beverage _beverage;
protected Beverage Bevy
{
get { return _beverage; }
}
protected CondimentDecorator(Beverage beverage)
{
_beverage = beverage;
}
}
课程更改为以下内容:
Mocha