Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / C#
Tip/Trick

Avoid Creating Inflexible Classes. Use Decorators.

Rate me:
Please Sign up or sign in to vote.
4.71/5 (12 votes)
9 Feb 2014CPOL2 min read 13.3K   12   2
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.

C#
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)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThanks Pin
AnotherKen10-Feb-14 18:54
professionalAnotherKen10-Feb-14 18:54 
QuestionThanks A Lot Pin
Matt U.10-Feb-14 5:05
Matt U.10-Feb-14 5:05 

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

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