【问题标题】:Decorator Pattern question C# / java装饰器模式问题 C#/java
【发布时间】:2011-05-19 00:31:04
【问题描述】:

我正在查看this 维基百科的文章,但无法理解它到底是如何工作的。仅仅通过查看代码就无法理解代码有点沮丧,我决定将代码移植到 c#(我是 .net,对不起,伙计们 :))。只需要进行一些小的修改(继承和扩展、super 的基础等)并运行应用程序。令我惊讶的是,我得到了以下输出:

Cost: 1 Ingredient: Coffee
Cost: 1 Ingredient: Coffee
Cost: 1 Ingredient: Coffee
Cost: 1 Ingredient: Coffee

只是好奇,任何 Java 开发人员都可以告诉我这里有什么不同以及为什么维基百科示例有效(当然,如果它确实像他们说的那样有效)。

    namespace ConsoleApplication1
{
 class Program
 {
  static void Main(string[] args)
  {

  Coffee sampleCoffee = new SimpleCoffee();
  Console.WriteLine("Cost: " + sampleCoffee.getCost() + " Ingredient: " + sampleCoffee.getIngredient());

        sampleCoffee = new Milk(sampleCoffee);
        Console.WriteLine("Cost: " + sampleCoffee.getCost() + " Ingredient: " + sampleCoffee.getIngredient());

        sampleCoffee = new Sprinkles(sampleCoffee);
        Console.WriteLine("Cost: " + sampleCoffee.getCost() + " Ingredient: " + sampleCoffee.getIngredient());

        sampleCoffee = new Whip(sampleCoffee);
        Console.WriteLine("Cost: " + sampleCoffee.getCost() + " Ingredient: " + sampleCoffee.getIngredient());

   Console.ReadKey();

  }
 }



//The Coffee Interface defines the functionality of Coffee implemented by decorator
public interface Coffee
{

    double getCost(); // returns the cost of coffee

    String getIngredient(); //returns the ingredients mixed with coffee
}

//implementation of simple coffee without any extra ingredients
public class SimpleCoffee : Coffee
{

    double cost;
    String ingredient;

    public SimpleCoffee()
    {
        cost = 1;
        ingredient = "Coffee";
    }

    public double getCost()
    {
        return cost;
    }

    public String getIngredient()
    {
        return ingredient;
    }
}



//abstract decorator class - note that it implements coffee interface
abstract public class CoffeeDecorator : Coffee
{

    protected Coffee decoratedCoffee;
    protected String ingredientSeparator;

    public CoffeeDecorator(Coffee decoratedCoffee)
    {
        this.decoratedCoffee = decoratedCoffee;
        ingredientSeparator = ", ";
    }

 public CoffeeDecorator()
 {

 }

    public double getCost() //note it implements the getCost function defined in interface Coffee
    {
        return decoratedCoffee.getCost();
    }

    public String getIngredient()
    {
        return decoratedCoffee.getIngredient();
    }
}

//Decorator Milk that mixes milk with coffee
//note it extends CoffeeDecorator
public class Milk : CoffeeDecorator
{

    double cost;
    String ingredient;

    public Milk(Coffee decoratedCoffee) : base(decoratedCoffee)
    {
        cost = 0.5;
        ingredient = "Milk";
    }

    public double getCost()
    {
        return base.getCost() + cost;
    }

    public String getIngredient()
    {
        return base.getIngredient() + base.ingredientSeparator + ingredient;
    }
}

//Decorator Whip that mixes whip with coffee
//note it extends CoffeeDecorator
public class Whip : CoffeeDecorator
{

    double cost;
    String ingredient;

 public Whip(Coffee decoratedCoffee)
  : base(decoratedCoffee)
    {
        cost = 0.7;
        ingredient = "Whip";
    }

    public double getCost()
    {
        return base.getCost() + cost;
    }

    public String getIngredient()
    {
        return base.getIngredient() + base.ingredientSeparator + ingredient;
    }
}

//Decorator Sprinkles that mixes sprinkles with coffee
//note it extends CoffeeDecorator
public class Sprinkles : CoffeeDecorator
{

    double cost;
    String ingredient;

    public Sprinkles(Coffee decoratedCoffee) : base(decoratedCoffee)
    {

        cost = 0.2;
        ingredient = "Sprinkles";
    }

    public double getCost()
    {
  return base.getCost() + cost;
    }

    public String getIngredient()
    {
  return base.getIngredient() + base.ingredientSeparator + ingredient;
    }
}

}

【问题讨论】:

标签: c# java design-patterns decorator


【解决方案1】:

这是一个有点冗长的例子,但我想我可以用 2 行来解释这个模式。 装饰器模式允许您包装现有的接口实现。 该模式的其他名称是包装器。

例如你有接口 Foo:

interface Foo {
    public int foo();
}

class SimpleFoo implements Foo {
    public int foo() {
        return 1;
    }
}

SimpleFoo.foo() 总是返回 1;

这是简单的说明符:

class DoubleFoo implements Foo {
    private Foo payload;
    public DoubleFoo(Foo payload) {
        this.payload = payload;
    }
    public int foo() {
        return 2 * payload.foo();
    }
}

DoubleFoo.foo() 装饰有效载荷 Foo。它将结果乘以 2。

显然它也可以用自己的实现来代替payload的实现。但这不是该模式的经典案例。

使用这种模式最广为人知的例子是 java 中的 IO:流、读取器和写入器都是包装器。例如 BufferedReader 向有效负载阅读器添加功能:它将数据读取到缓冲区。

【讨论】:

    【解决方案2】:

    您使用的是 CoffeeDecorator 的 getCost() 方法,而不是实现类的 getCost() 方法。您需要查看如何覆盖该方法。

    【讨论】:

      【解决方案3】:

      您忘记声明 getCost 和 getIngredient virtual 并在派生版本中使用 override 关键字。你这样做的方式,你只是“重载”了方法。

      【讨论】:

      • 这并不是真正的超载 - 它是阴影/隐藏。
      【解决方案4】:

      是的 - 在 Java 中方法默认是虚拟的,但在 C# 中不是。

      您应该在编译代码时收到警告,谈论“new”修饰符。那应该给你一个线索。目前,您的 Milk (等)方法是 hidingshadowing CoffeeDecorator 中的方法 - 它们不会被多态调用。

      您需要使用virtual 修饰符将CoffeeDecorator 方法设为虚拟,然后使用override 修饰符在Milk(等)中显式覆盖它们。

      // In CoffeeDecorator
      public virtual double getCost()
      {
          return decoratedCoffee.getCost();
      }
      
      // In Milk
      public override double getCost()
      {
          return base.getCost() + cost;
      }
      

      【讨论】:

      • Nooo ...乔恩,你比我快 10 秒 :'(
      • 啊。我应该看看编译器:)。不知道默认情况下Java中的一切都是虚拟的。这让我想到另一个问题.. 你能在 java 中使方法“非”虚拟吗?
      • @Daniel Perez,你可以通过声明 final.
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-04
      • 2020-05-13
      相关资源
      最近更新 更多