Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Decorator Design Pattern

4.73/5 (10 votes)
8 Sep 2009CPOL3 min read 56K   238  
This article shows a case study about how we can use the Decorator Pattern for a daycare center reward system.

Background

Again, my favorite place - Elizabeth's daycare center.

In Elizabeth's daycare center, there are many reward programs for the kids each week. For example: three types of awards are given to kids for their achievements at the end of each week. Depending on how the kids perform each week, the kids could have different prizes:

  1. All the kids will have a sticker at the end of the week as a prize.
  2. A kid could have a certificate posted on his/her cubicle if the kid passed potty training.
  3. The kid could also have a free book when she/he becomes the KID of the WEEK.

Remember, kids may have different prizes at the end of each week. It really depends on what the kid has done that week. That means the awards are dynamically added to the kids every week, and they can also be removed the next week.

Introduction

The Decorator design could help us when we try to expend the additional works for a particular activity. Based on different levels (or conditions) of the same object, we may want to expend the decoration for the same method. For example: a one year old baby will smile when she is happy. Once she is 2 years old, she could smile and jump to express her happiness. When she is 3 years old, she could smile, jump, and also talk when she is happy. However from the client side, the client will not notice what additional process has been attached when consuming an object. The client only knows the baby is happy, how the baby describes himself/herself as happy will depend on his/her age.

Decorator Design Pattern Structure

Decorator.JPG

Class Diagram

Decorator_Class.JPG

Implementation Code

Component Classes

Kid

The Kid class is an abstract base component, it has an abstract method to show all the prizes for a kid.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace Decorator
    {
        public abstract class Kid
        {
            public string Name { get; set; }
            public abstract void ShowMyPrizes();
        }
    }
}

GoodKid

GoodKid is the concrete component class that inherits from the Kid class. It actually provides the default implementation for ShowMyPrizes().

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace Decorator
    {
        //Concreate Kid
        public class GoodKid : Kid
        {
            public override void ShowMyPrizes()
            {
                Console.WriteLine("My name is {0} . 
            I am a good kid! I get a sticker this week", Name);
            }
        }
    }
}

Decorator Classes

KidDecorator

The KidDecorator contains a Kid object that will allow the Decorator class to extend the default method (in this case, it would be ShowMyPrizes()). It's an abstract class that only provides the basic protocol; a concrete Decorator class has to be developed to implement the extension details.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace Decorator
    {
        public abstract class KidDecorator : Kid
        {
            //an instance of kid. 
            Kid aKid;

            public KidDecorator(Kid kid)
            {
                aKid = kid;
            }

            //show the deafult prize for aKid object
            public override void ShowMyPrizes()
            {
                aKid.ShowMyPrizes();
            }
        }
    }
}

PottyTrainedKid and KidofWeek_Kid

PottyTrainedKid and KidofWeek_Kid are the concrete Decorator classes to plug the additional works into the default method.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace Decorator
    {
        public class PottyTrainedKid : KidDecorator
        {
            public PottyTrainedKid(Kid aKid)
                : base(aKid)
            {
            }

            public override void ShowMyPrizes()
            {
                //call base(deafault) method.
                base.ShowMyPrizes();
                //addtional method
                ShowMyExtendPrize();
            }

            private void ShowMyExtendPrize()
            {
                Console.WriteLine("Since I am a potty trained kid, " + 
                  "I also have an certificate stamped on my cube for this week");
            }
        }
    }
}
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace www.askbargains.com
{
    namespace Decorator
    {
         public class KidofWeek_Kid : KidDecorator
        {

            public KidofWeek_Kid(Kid aKid)
                : base(aKid)
            {
            }
            public override void ShowMyPrizes()
            {
                //call base(deafault) method.
                base.ShowMyPrizes();
                //addtional method
                ShowMyAdditionalPrize();

            }

            private void ShowMyAdditionalPrize()
            {
                Console.WriteLine("Since I am a kid of this month, I also " + 
                                  "have a free book as a prize for this week");
            }
        }
    }
}

Client App

From my client application, I simulate a Use Case as below.

In the first week, Elizabeth is just a good kid. In the second week, Elizabeth passes the potty training program. In the third week, Elizabeth passes the potty training program and also becomes kidofweek. In the fourth month, she is again kidofweek, but doesn't pass the potty training again.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using www.askbargains.com.Decorator;

namespace www.askbargains.com
{
    namespace Client
    {
        class Program
        {
            static void Main(string[] args)
            {

                //Kid Elizabeth is created
                GoodKid Elizabeth = new GoodKid();
                Elizabeth.Name = "Elizabeth";

                //First week. Elizabeth is a good kid only. She deserve her default prize.
                DisplayReport(Elizabeth, "This is first week.");

                //In the second week, Elizabeth pass the potty training.
                Kid pottyKid = new PottyTrainedKid(Elizabeth);
                DisplayReport(pottyKid, "This is second week.");

                //In the third week, Elizabeth is both potty trained
                //and kid of the week,
                //She will have default , potty trained and kidofweek prizes.
                Kid kidofweek = new KidofWeek_Kid(pottyKid);
                DisplayReport(kidofweek, "This is third week.");


                //In the four week, Elizabeth is kid of the week,
                //She will have default and kidofweek prizes.
                Kid newKid = new KidofWeek_Kid(Elizabeth);
                DisplayReport(newKid, "This is fourth week.");

                Console.Read();

            }

            //Helper method to show the report.
            private static void DisplayReport(Kid anyKid, string weekMessage)
            {
                Console.WriteLine(weekMessage);
                anyKid.ShowMyPrizes();
            }
        }
    }
}

Once you execute the client app, you can see all the prizes for each week that Elizabeth has earned. Cool!

Decorator_output.JPG

Conclusion

In this article, I demonstrated how we can use the Decorator Pattern to achieve the implementation for a daycare reward system. I also used the daycare center for the Chain of Responsibility Pattern in my other article.

History

  • 2 September, 2009: Initial post.

License

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