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

Yet Another Lesson about Design Patterns

, 9 Jun 2014
Rate this:
Please Sign up or sign in to vote.
When should you switch from factory pattern to decorator pattern?

Introduction                                    


I am back today with a new old topic: Design Patterns! So this started with an article posted by a good friend of mine about his way of implementing the factory pattern.

Although there is nothing to approach about his way, something kept itching me to write a second article about another design pattern that might fit in this situation as well.

Let’s start by reviewing the task:

The Straw hats pirates (the pirate crew in the one piece anime) contacted my company to write a software to automate pizza preparation. Sanji explained that in order to prepare pizza, we need to:

  1. Prepare the pizza dough
  2. Prepare the sauce
  3. Add pizza sauce and pizza ingredients according to each person’s desires
  4. Put the pizza in the oven

In order to complete the task, my friend wrote a Pizza maker base class where inherited classes redefine the AddIngredient method to suit each person’s choices.

For example, if the person is Nami, then ingredients will be cheese and olives. To add his own touch, my friend added a configuration section to make class adding totally on the fly. There is no doubt that the solution is flexible and neat but what happens when more people join in the team? Or when current crew feels like tasting something new? Are we going to create sub classes for each possibility like this:

It’s sure not a good solution because with 3 ingredients (Olive, cheese and meat), we can have up to 7 different combinations (result = n! / [(n-k)! k!] Where n is total number of ingredients and k is the number of the picked ingredients), Imagine what will happen with 4 or 5 ingredients => explosion in number of sub classes.

What should we do then? It’s time to introduce the Decorator Pattern!

The decorator pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Don’t worry if you don’t understand this, I myself didn’t when I first read it but this example will help clear things for us.

Component: is the father of all, can exist on his own or equipped with decorators.

ConcreteComponent: son of component, can do more cool stuff than his old father.

Decorator: sort of the mother of all decorators, it teaches them the ABCs of life without precising how to act.

ConcreteDecorator: the real deal where decorator operations are defined.

Let’s put this in action and see what we get:

Explanation

Our main goal is to treat all pizza objects the same (this is why everything inherits from Pizza class) and provide a mecanism to decorate our objects in a way that makes them different from one another.

The Pizza here is your component, it’s the base class of the object you want to decorate.

    public class Pizza
    {
        public void PreparePizza()
        {
        }
 
        private void AddSauce()
        {
        }
 
        public virtual void MakeDough()
        {
        }
 
        private void PutInOven()
        {
        }
 
        private void PrepareSauce()
        {
        }
 
        public virtual string GetIngredients()
        {
            return "Pizza with ";
        }
    } 

Three classes derive directly from Pizza: the first two (CrustyDoughPizza and ThickDoughPizza) are considered ConcreteComponents since they extend Pizza’s behavior (Exp: creates a different more delicious dough).

    public class ThickDoughPizza : Pizza
    {
        public override void MakeDough()
        {
            //Make thick dough
        }
 
        public override string GetIngredients()
        {
            return "Thick Dough Pizza with ";
        }
    } 
    public class CrustyDoughPizza : Pizza
    {
        public override void MakeDough()
        {
            //Make CrustyDough
        }
 
        public override string GetIngredients()
        {
            return "Crusty Dough Pizza with ";
        }
    } 

The last third sub class is PizzaIngredient which is the base class of any pizza ingredient since it defines what is expected from an ingredient. In this case, it forces every ingredient to override the behaviour of GetIngredients method of the Pizza class in order to have its own special implementation. Notice also the existence of a pizza field (MyPizza), its use will be covered later.

    public abstract class PizzaIngredient : Pizza
    {
        public Pizza MyPizza {get; set;}
        public override abstract string GetIngredients();
    }  

Finally, by implementing PizzaIngredient methods, subclasses share the same methods but with custom functionalities therefore they can decorate Pizza objects rendering equally different but without compromising their origin. Hold on a sec, how can they decorate Pizza objects?

    public class Cheese : PizzaIngredient
    {
        public Cheese(Pizza pizza)
        {
            MyPizza = pizza;
        }
 
        public override string GetIngredients()
        {
            return MyPizza.GetIngredients() + " Cheese";
        }
     }  
   public class Olive : PizzaIngredient
    {
        public Olive(Pizza pizza)
        {
            MyPizza = pizza;
        }
 
        public override string GetIngredients()
        {
            return MyPizza.GetIngredients() + " Olive";
        } 
    }
       public class Meat : PizzaIngredient
    {
        public Meat(Pizza pizza)
        {
            MyPizza = pizza;
        }
        public override string GetIngredients()
        {
            return MyPizza.GetIngredients() + " Meat";
        }
    }

Excellent question, each ingredient must have a constructor which takes a Pizza argument. Decoration occurs when this argument is assigned to the MyPizza property. If we need to decorate an already decorated object, all we need to do is pass in the decorated object as an argument (it's pizza after all) to the custom constructor of the decorator.

Let’s try to make a CrustyDoughPizza with olive only:

CrustyDoughPizza myPizza = new CrustyDoughPizza();
//Decorate the pizza with olive
Olive olivePizza = new Olive(myPizza);
var pizzaIngredients = olivePizza.GetIngredients();
//expected answer = Crusty pizza with olive

How? One Word: Recurrence!

When we call GetIngredients of Olive, it will bounce to call GetIngredients of CrustyPizza before it replies back to us just like in the schema below.

Let’s now do a more complicated thing: a ThickDough Cheese and Meat Pizza:

ThickDoughPizza myPizza = new ThickDoughPizza();
//Decorate the pizza with Meat
Meat pizzaWithMeat = new Meat(myPizza);
//Decorate the pizza with cheese
Cheese pizzaWithCheese = new Cheese(pizzaWithMeat);
var pizzaIngredients = pizzaWithCheese.GetIngredients();
//expected answer = Crusty pizza with meat cheese

I believe this is a good solution for a situation when we don’t have control over the choices of the client. The decorator pattern makes components and decorators behave the same way abstracting differences and eliminating useless conditional tests but you may want to be careful about how you want to adjust your implementation to your case through interfaces and abstract classes.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Bilel Msekni
Engineer
France France
I am a software engineer trying to improve his code quality through best practises and design patterns.
Follow on   LinkedIn

Comments and Discussions

 
QuestionFluent API PinmemberChris Richner9-Jun-14 20:06 
QuestionExpected answer PinmemberCarlos A. Oliveira5-Jun-14 6:29 
AnswerRe: Expected answer PinprofessionalBilel Msekni7-Jun-14 23:56 
QuestionThe code samples need sorting. Pinmembercjb1101-Jun-14 20:48 
AnswerRe: The code samples need sorting. PinprofessionalBilel Msekni2-Jun-14 9:47 
GeneralRe: The code samples need sorting. Pinmembercjb1102-Jun-14 20:58 
GeneralMy vote of 5 PinprofessionalEmre Ataseven1-Jun-14 18:49 

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
Web04 | 2.8.140721.1 | Last Updated 9 Jun 2014
Article Copyright 2014 by Bilel Msekni
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid