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」に更新され、b を Mocha で装飾すると (b = new Mocha(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クラスは実際には version メソッドを非表示にしますIBeverage(GetDescription()技術的にはpublic new abstract string GetDescription();.

  • クラスを変数に割り当てることでMochaクラスを として分類しています (以前に を介して IBeverage として定義しました 。メソッドのバージョンが実際に起動するものです (メソッドのオーバーライドを完全に無視します) 。IBeveragebIBeverage b = new Espresso()IBeverageGetDescription()MochaCondimentDecorator 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(); すると、デバッグ時に 使用を避けることができます。これは自動的に挿入されます。CtrlF5 "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 に答える