I was looking at this wikipedia article, and couldn't understand how the hell that was working. A little bit frustrated not being able to understand the code just by looking at it, i dedided to port the code to c# (i'm .net, sorry guys :)). Just some minor modifications were needed (inherits and extends, base for super, etc) and run the app. To my surprise, i got the following output :
Cost: 1 Ingredient: Coffee
Cost: 1 Ingredient: Coffee
Cost: 1 Ingredient: Coffee
Cost: 1 Ingredient: Coffee
Just curious, can any java dev tell me what's different here and why the wikipedia example works (if it does work as they say it does, of course)开发者_运维技巧.
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;
}
}
}
Yes - methods are virtual by default in Java, but not in C#.
You should have received warnings when compiling your code, talking about the "new" modifier. That should have given you a clue. Currently your Milk
(etc) methods are hiding or shadowing those in CoffeeDecorator
- they're not being called polymorphically.
You'd need to make the CoffeeDecorator
methods virtual with the virtual
modifier, and then explicitly override them in Milk
(etc) with the override
modifier.
// In CoffeeDecorator
public virtual double getCost()
{
return decoratedCoffee.getCost();
}
// In Milk
public override double getCost()
{
return base.getCost() + cost;
}
You forgot to declare getCost and getIngredient virtual
and use the override
keyword in the derived verions. The way you did it, you just "overload" the methods.
You're hitting CoffeeDecorator's getCost() method rather than the implementing class' getCost() method .. you need to look at how you're overriding the method.
It's a bit verbose example but I think I can explain the pattern in 2 lines. Decorator pattern allow you to wrap existing implementation of interface. Other name of the pattern is wrapper.
For example you have interface Foo:
interface Foo {
public int foo();
}
class SimpleFoo implements Foo {
public int foo() {
return 1;
}
}
The SimpleFoo.foo() always returns 1;
Here is the simple decarator:
class DoubleFoo implements Foo {
private Foo payload;
public DoubleFoo(Foo payload) {
this.payload = payload;
}
public int foo() {
return 2 * payload.foo();
}
}
DoubleFoo.foo()
decorates the payload Foo. It multiplies the result by 2.
Obviously it can also replace the implementation of payload by its own implementation. but it is not a classic case of the pattern.
The most well known example of usage of this pattern is IO in java: streams, readers and writers are all wrappers. For example BufferedReader adds functionality to payload reader: it reads data to buffers.
精彩评论