2
\$\begingroup\$

I have a model class Product that gets populated from a service DTO object (using AutoMapper). The service is used to power many different applications and for each one the Product Model might need to behave a bit differently. After a bit of research I decided the 'Decorator' pattern might be a good choice to accompany these differences. Here is how I currently have implemented it.

These classes are part of a library that gets included in each of the applications

public class Product
{
    public virtual string ProductName { get; set; }
    public virtual Decimal Price { get; set; }
    public virtual string ImageUrl { get; set; }

    public virtual string GetImageUrl(){
        return ImageUrl;
    }
}

public abstract class ProductDecorator : Product
{
    private Product decoratedProduct; // the product being decorated

    protected ProductDecorator(Product decoratedProduct)
    {
        this.decoratedProduct = decoratedProduct;
    }

    public override string ProductName
    {
        get
        {
            return decoratedProduct.ProductName;
        }
        set
        {
            decoratedProduct.ProductName = value;
        }
    }
    public override decimal Price
    {
        get
        {
            return decoratedProduct.Price;
        }
        set
        {
            decoratedProduct.Price= value;
        }
    }
    public override string ImageUrl
    {
        get
        {
            return decoratedProduct.ImageUrl;
        }
        set
        {
            decoratedProduct.ImageUrl= value;
        }
    }
}

In each application there may be need to slightly change the model.

public class FooProduct : Model.ProductDecorator
{
    public FooProduct (Model.Product product) :base(product){}

    //we want to serve the image through a cdn
    public override string GetImageUrl()
    {
        return string.Format("cnd.network.com?url={0}", HttpUtility.HtmlEncode(ImageUrl)); 
    }
}

Usage would be something like this:

 FooProduct prod = new FooProduct(productClient.GetProduct(23213));
 Response.Write(prod.GetImageUrl());

This is my first time using this pattern and I'm not sure if I've got it quite right or if it really applies to this scenario. Are there any foreseeable issues with this solution?

Update

I think I need to explain a little more how I got here.

I created a class library to encapsulate an wcf service. The library takes the DTO's returned by the service and using AutoMapper, maps the DTO to the Product class. The issue is that the product model needs to be a bit different for each of the applications consuming the service. Just using simple inheritance not quite right because a FooProduct needs to pretty much be exactly the same as a Product but just handle the data a bit differently.

// this returns a Product, but for this site I need a FooProduct
var product = Client.GetProduct(232); 

This problem is probably a result of bad design in lower layers and from the use of AutoMapper in Client Library.

\$\endgroup\$
  • 2
    \$\begingroup\$ This doesn't look right; you're creating an abstract class which cannot be instantiated, and inheriting from a class that can. In addition, when I think of "decoration," I think of adding attributes to classes, not class inheritance. \$\endgroup\$ – Robert Harvey Mar 20 '13 at 17:48
  • \$\begingroup\$ @RobertHarvey thanks for moving it here. I wasn't aware of this site! \$\endgroup\$ – NSjonas Mar 20 '13 at 18:51
  • 1
    \$\begingroup\$ It's a way to use the decorator pattern. :) Whether it's a good candidate for it is another question. I'd keep image base urls in some config instead and have a helper generate the full urls somewhere in the client project. Hence no need for FooProduct. BUT, if you have more "business-related" things you need to decorate a product with, I'd move the decoration into the service. Use some kind of reflection or IoC to find the relevant decorator(s) and do it before the UI/client code gets it. It can continue using it as a Product. \$\endgroup\$ – Lars-Erik Mar 21 '13 at 12:52
4
\$\begingroup\$

I think you've misunderstood what decorator pattern is. As described in Wikipedia

"In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class."

What you are doing is simple inheritance.

You can do this with an interface, but you don't have too. You have to ask yourself the question "Do I need to use the Product class?" If you do, then leave it as is, if you don't, make it an interface, or mark it abstract.

I'm going to assume you need it.

Your class definition looks the same:

public class Product
{
    public virtual string ProductName { get; set; }
    public virtual Decimal Price { get; set; }
    public virtual string ImageUrl { get; set; }

    public virtual string GetImageUrl()
    {
        return ImageUrl;
    }
}

You would then inherit from this class for any other class you need:

public class FooProduct : Product
{
    //we want to serve the image through a cdn
    public override string GetImageUrl()
    {
        return string.Format("cnd.network.com?url={0}", HttpUtility.HtmlEncode(ImageUrl)); 
    }
}

You can now use either class as required:

var productInstance = new Product();

var fooProductInstance = new FooProduct();

The nice part is, you can now pass FooProduct in anywhere a Product is expected:

public void DoSomethingWithProduct(Product product)
{
    // Do some processing
}

Can be called:

processor.DoSomethingWithProduct(product);

or

processor.DoSomethingWithProduct(fooProduct);
\$\endgroup\$
  • \$\begingroup\$ The issue with this design is that I don't have control on the Product instantiation since its coming from a Client Library using Automapper to go from my DTO's to the Product class. I guess I was trying to use the Decorator pattern so developers don't have to write mapping code every time they extend the Product. (essentially I'm an looking for some way to cast from a parent class to a derived class). Since these models will be passed into controls they will never build additional logic but instead just override methods \$\endgroup\$ – NSjonas Mar 20 '13 at 18:48
  • \$\begingroup\$ How do you differentiate between Product and FooProduct then? \$\endgroup\$ – Jeff Vanzella Mar 20 '13 at 18:55
  • \$\begingroup\$ Not quite sure what you mean by differentaite... Also I can still pass FooProduct anywhere that product is expected with the code in my answer. This solution probably arose from other issues in lower layers of the design. \$\endgroup\$ – NSjonas Mar 20 '13 at 18:59
  • 1
    \$\begingroup\$ From reading that Wiki it appears the OP has done exactly what it has said. The Window example they gave is essentially exactly the implementation here. What would be different from the Wiki Window example (apart from the fact Window is abstract) to the OP example?? \$\endgroup\$ – dreza Mar 20 '13 at 19:10
  • 1
    \$\begingroup\$ Gotcha! From what I understood from your original question, I thought you were just trying to inherit Product into a FooProduct. I don't think there is much you can do if you need the different classes, but can't get them from the lower level. Is the imdateurl the only property that can be changed in the Product class, or is this just an example? \$\endgroup\$ – Jeff Vanzella Mar 20 '13 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.