Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Avoid Creating Inflexible Classes. Use Decorators.

0.00/5 (No votes)
9 Feb 2014 3  
Consider the Decorator Pattern!

Introduction

Far too often, we (fellow programmers) make the decision to use standard inheritance when adding new functionality to classes. However, there are many occasions in which this leads to inflexible code. We then scratch our heads and wonder how it happened.

Let me explain with an example. Suppose we have an application that calculates the price of cookies. We create hierarchies like Cookie, ChocolateChipCookie, PeanutButterCookie, etc. Let's say we have a GetPrice() overloaded method for each class. Everything works great until the cookie factory manager tells us that now they are manufacturing peanut butter chocolate chip cookies. If we then start creating classes (such as a ChocolateChipPeanutButterCookie), the code becomes unruly rather quickly (in particular trying to separate class responsibilities). So, this design was not flexible enough to handle this simple request.

However, using the decorator pattern is perfect for this scenario. The decorator pattern allows us to add behavior to objects at run-time without directly relying on inheritance. A decorator conforms to the component it is decorating. An example below will make this clear.

Using the Code

In this example, let us create some cost requirements to make it more concrete. Let us assume the base cost of a cookie with its core ingredients is 30 cents, the cost to add chocolate chips is 20 cents, and 40 cents to include peanut butter (my favorite).

Please look at the code below.

Both the ChocolateChipCookie and PeanutButterCookie classes allow for an instance of an ICookie to be passed in the constructor. ChocolateChipCookie decorates the Cookie and adds the price of the chocolate chips to the total. The PeanutButter cookie does the same.

So, to create a cookie that has both chocolate chips and peanut butter, just create a new PeanutButterCookie(chocolateChipCookie);

This allows for your classes to contain any combinations without a rigid hierarchy.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
 
namespace ConsoleApplication1
{
    class Program
    { 
        private interface ICookie
        {
            float GetPrice();
        }
 
        private class Cookie : ICookie
        {
            public float GetPrice()
            {
                return (float) 0.30;
            }
        }
 
        //Decorator 1
        private class ChocolateChipCookie : ICookie
        {
            private ICookie _cookie;
            private float _priceofChocolateChip = (float) 0.20;
 
            public ChocolateChipCookie(ICookie cookie)
            {
                _cookie = cookie;
               
               
            }
 
            public float GetPrice()
            {
               
                return _cookie.GetPrice() + _priceofChocolateChip;
            }
        }
 
        //Decorator 2
        private class PeanutButterCookie : ICookie
        {
            private ICookie _cookie;
            private float _priceofPeanutButter = (float) 0.40;
 
            public PeanutButterCookie(ICookie cookie)
            {
                _cookie = cookie;
               
            }
 
            public float GetPrice()
            {
                //get price and add to it
                return _cookie.GetPrice() + _priceofPeanutButter;
            } 
        }
        static void Main(string[] args)
        {
 
            var chocolateChipCookie = new ChocolateChipCookie(new Cookie());
            var peanutButterCookie = new PeanutButterCookie(new Cookie());
 
            // easy to create combinations! 

            var peanutButterchocolateChipCookie = new PeanutButterCookie(chocolateChipCookie);
 
            Console.WriteLine("price of chocolate chip cookie: " 
            	+ chocolateChipCookie.GetPrice());
            Console.WriteLine("price of peanutbutter cookie: " 
            	+ peanutButterCookie.GetPrice());
            Console.WriteLine("price of peanutbutter chocolate chip cookie: " 
            	+ peanutButterchocolateChipCookie.GetPrice());
        }
    }
}

When executed, you should see the following output:

Price of chocolate chip cookie: 0.5 
Price of peanutbutter cookie: 0.7
Price of peanutbutter chocolate chip cookie: 0.9

A helpful way to identify when to use the decorator pattern is to determine if the new behavior is an option or not. If they are "options" where different combinations are allowed, then the decorator pattern is the way to go.

I am very hungry for some reason, so I am going to get going... Happy coding.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here