Click here to Skip to main content
Click here to Skip to main content

Avoid Creating Inflexible Classes. Use Decorators.

, 9 Feb 2014
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Bruce S Oberhand

United States United States
No Biography provided

Comments and Discussions

 
QuestionThanks PinmemberAnotherKen10-Feb-14 18:54 
QuestionThanks A Lot PinmemberMatt U.10-Feb-14 5:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 9 Feb 2014
Article Copyright 2014 by Bruce S Oberhand
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid