zzfima
zzfima

Reputation: 1565

Decorator Design Pattern without concrete objects

I see Decorator DP examples, but all of them imply, that concrete object will be at the end of chain:

IProduct prod2 = new Sugar(new Sugar(new Sugar(new Milk(new DarkRoast()))));
Console.WriteLine("I am {0}, my cost is {1}", prod2.Name(), prod2.Cost());

DarkRoast is a concrete object, other object - is a decorators. So, I (or client) need to remember who is at the and of creation chain.

So, by default, I can not do:

IProduct prod2 = new Sugar(new DarkRoast(new Sugar(new Milk(new Sugar()))));
Console.WriteLine("I am {0}, my cost is {1}", prod2.Name(), prod2.Cost());

But, the meaning (I think) is same: Dark roast cofee + 3 sugar + 1 milk.

So, I redesign it to:

public interface IProduct {
    double Cost();
    string Name();
}

public class DarkRoast : IProduct {
    IProduct _product;

    public DarkRoast(IProduct prod = null) { _product = prod; }

    public double Cost() {
        if (_product != null) { return 2.5 + _product.Cost(); }
        else { return 2.5; }
    }

    public string Name() {
        if (_product != null) { return "DarkRoast " + _product.Name(); }
        else { return "DarkRoast "; }
    }
}

public class Milk : IProduct {
    IProduct _product;

    public Milk(IProduct prod = null) { _product = prod; }

    public double Cost() {
        if (_product != null) { return 0.5 + _product.Cost(); }
        else { return 0.5; }
    }

    public string Name() {
        if (_product != null) { return "With milk " + _product.Name(); }
        else { return "With milk "; }
    }
}

public class Sugar : IProduct {
    IProduct _product;

    public Sugar(IProduct prod = null) { _product = prod; }

    public double Cost() {
        if (_product != null) { return 0.2 + _product.Cost(); }
        else { return 0.2; }
    }

    public string Name() {
        if (_product != null) { return "With sugar " + _product.Name(); }
        else { return "With sugar "; }
    }
}

It is make me possible do

IProduct prod2 = new Sugar(new DarkRoast(new Sugar(new Milk(new Sugar()))));
Console.WriteLine("I am {0}, my cost is {1}", prod2.Name(), prod2.Cost());

So, I do not need to remember order of stuff. And, I think all stuff here is decorators (is it a bad idea?) What are disadvantages of these implementation?

Upvotes: 1

Views: 151

Answers (1)

Don Roby
Don Roby

Reputation: 41137

This looks reasonable to me, but it might be slightly improved by adding a concrete Null Object used as the default delegate object and thus avoiding the null checks.

Modulo my knowledge of your language syntax (it's a language I don't actually use), this would look something like this:

public class Empty : IProduct
{

    public double Cost()
    {
        return 0.0;
    }

    public string Name()
    {
        return "";
    }
}


public class Sugar : IProduct
{
    IProduct _product;


    public Sugar(IProduct prod = null) 
    {  
        _product = (prod == null) ? new Empty() : prod;
    }

    public double Cost()
    {
        return 0.2 + _product.Cost();
    }

    public string Name()
    {
        return "With sugar " + _product.Name();
    }
}

Upvotes: 1

Related Questions