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

An Introduction to Design Patterns using the Decorator Pattern

, 3 Jan 2006
Rate this:
Please Sign up or sign in to vote.
An introduction to the Decorator pattern.

Introduction - What are Design Patterns?

In order to understand what design patterns are, you first need a basic understanding of what software development really is. At its core, software design and development is nothing more than an attempt to solve a problem, usually a business or scientific one, using computing. This is the reason we refer to software as solutions. There have been millions of software solutions created to solve just as many problems. Many of these problems are identical, or similar enough to be nearly identical. In most cases, there are usually several potential solutions to any one of these problems. However, all solutions are not created equal, and the design for a solution is as important, if not more so, than the code that actually implements that solution. Problems change and evolve, and the solutions that solve those problems must be able to change and adapt easily if they are going to be of any real value. A good design supports the development of software that is able to adapt and change easily as the problem it is solving changes. A good design also provides the ability to reuse aspects of the solution in other places. This is the basis for why Design Patterns were created. They provide well defined, flexible, time tested solutions to the common problems that software attempts to solve.

Design Patterns are nothing new. They have been around for decades. They can be implemented in nearly any object oriented language. They require little more than an understanding of the problem you are trying to solve and an understanding of the pattern that solves that problem. As mentioned previously, they use designs that have been tried and tested and have shown themselves to be good solutions for the respective problems that they solve. There are literally hundreds of design patterns that have been defined. The most well known design patterns were defined by a group of talented software designers now known as “The Gang of Four”. Having a good repertoire of design patterns under your belt means that when you are presented with a problem, you don’t need to spend as much time figuring out how to solve it. You simply identify the problem and apply the pattern to it. They also give you a common base of terminology with which to describe your solution to other developers that are helping to solve the problem. If you are working with a group of developers that are familiar with design patterns, you don’t have to spend time explaining the intricacies of a solution; you need only specify the pattern you think best solves the problem. If everyone is familiar with the pattern you are proposing, you are much further along in the design process. Best of all, you know that because you used a pattern, your solution has a decent chance of being a good one.

Having said all that, let’s take a look at a design pattern, the Decorator Pattern. The decorator pattern solves problems where you have an object whose state or behavior can be modified by other objects that attach to or “decorate” it. Examples of this would be pizzas and their toppings, cars and their options, or streams and what to read or write to and the format they do it in. To help this make more sense, let’s take a look at a solution to a problem where we need the ability to decorate a base car with one or more non-standard features in order to come up with its sale price and description.

Using the code

Solution Diagram

  1. The first thing that we have to do is define a common type that will be used for both the cars themselves and the options we will use to decorate the cars with. This type will be the base for all of the cars that we will decorate. To do that, we create the following:

    (It should be noted that it is not necessary to define both an interface for cars and an abstract, one or the other or both will suffice. What to use depends solely on the design considerations of your application.)

    /// <span class="code-SummaryComment"><summary>
    </span>
    /// Defines the members necessary for any
    /// Car and/or Car option implementation.
    /// <span class="code-SummaryComment"></summary>
    </span>
    interface ICar
    {
        double Cost {get;}
        string Description { get; }
    }
    
    /// <span class="code-SummaryComment"><summary>
    </span>
    /// Provides the base type for concrete cars
    /// and concrete car options
    /// <span class="code-SummaryComment"></summary>
    </span>
    abstract class Car:ICar
    {
        private double _cost = -1;
        private string _desc = "abstract car.";
    
        public virtual double Cost
        {
            get { return _cost; }
        }
    
        public virtual string Description
        {
            get { return _desc; }
        }
    }
  2. After we have defined the base functionality for our cars and their options, we need to define a base type for the options themselves:
    /// <span class="code-SummaryComment"><summary>
    </span>
    /// Provides the base type for concrete car options
    /// that will be used to decorate concrete cars
    /// <span class="code-SummaryComment"></summary>
    </span>
    abstract class CarOption:Car
    {
        double _cost = -1;
        string _desc = "abstract car option";
        public override string Description
        {
            get { return _desc; }
        }
    
        public override double Cost
        {
            get { return _cost; }
        }
    }

    CarOption (above) implements Car and overrides its behavior. This type will be the base for all the classes that we will use to decorate a car with.

  3. Now, we will create a concrete implementation of a car:
    /// <span class="code-SummaryComment"><summary>
    </span>
    /// Provides a concrete implementation
    /// of the Ferrri Spider car.
    /// <span class="code-SummaryComment"></summary>
    </span>
    class FerrariSpider:Car
    {
        double _cost = 250000;
        string _description = "Ferrari Spider";
        
        public override double Cost
        {
            get 
            { 
                 return _cost;
            }
        }
    
        public override string Description
        {
            get
            {
                return _description;
            }
        }
    }

    We have created a FerrariSpider class which implements our base Car class and overrides its behavior with the Ferrari specific behavior, namely the cost and desciption of a Ferrari Spider with no options.

  4. Now, we need to define some options that we want to decorate our Ferrari with:

    We are going to create Turbo, Alloy Wheels, and Leather options for our Ferrari (or any other car that we might want to use them on).

    /// <span class="code-SummaryComment"><summary>
    </span>
    /// Provides a concrete implementation
    /// of the Alloy Wheels car option.
    /// <span class="code-SummaryComment"></summary>
    </span>
    class OptionAlloyWheels : CarOption
    {
        double _cost = 4564.42;
        string _description = "Alloy Wheels, ";
        Car _car;
    
        public OptionAlloyWheels(Car car)
        {
            _car = car;
        }
    
        public override double Cost
        {
            get
            {
                return _car.Cost + _cost;
            }
        }
    
        public override string Description
        {
            get
            {
                return _car.Description + _description;
            }
        }
    }
    
    /// <span class="code-SummaryComment"><summary>
    </span>
    /// Provides a concrete implementation
    /// of the Turbo car option.
    /// <span class="code-SummaryComment"></summary>
    </span>
    class OptionTurbo:CarOption
    {
        double _cost = 7000.24;
        string _description = "Turbo, ";
        Car _car;
    
        public OptionTurbo(Car car)
        {
            _car = car;
        }
    
        public override double Cost
        {
            get
            {
                return _car.Cost + _cost;
            }
        }
    
        public override string Description
        {
            get
            {
                return _car.Description + _description;
            }
        }
    }
    
    /// <span class="code-SummaryComment"><summary>
    </span>
    /// Provides a concrete implementation of the Leather car option.
    /// <span class="code-SummaryComment"></summary>
    </span>
    class OptionLeather : CarOption
    {
        double _cost = 1000;
        string _description = "Leather Seats, ";
        Car _car;
    
        public OptionLeather(Car car)
        {
            _car = car;
        }
    
        public override double Cost
        {
            get
            {
                return _car.Cost + _cost;
            }
        }
    
        public override string Description
        {
            get
            {
                return _car.Description + _description;
            }
        }
    }

    Each of our options implement our CarOption class and override its default behavior. Each option also has a local reference to a Car type that will be used to hold a reference to the car that is to be decorated. The car instance is handed to the option via the option’s constructor.

  5. All that is left to be done now is to create an instance of our spider and give it some options by decorating it with the various option classes that we have created.
    /// <span class="code-SummaryComment"><summary>
    </span>
    /// Driver program for creating cars
    /// and decorating them with options.
    /// <span class="code-SummaryComment"></summary>
    </span>
    class Program
    {
        static void Main(string[] args)
        {
            //create our car
            Car car = new FerrariSpider();
            //decorate it with the leather option
            car = new OptionLeather(car);
            //decorate it with the alloy wheels option
            car = new OptionAlloyWheels(car);
            //decorate it with the turbo option
            car = new OptionTurbo(car);
            Console.WriteLine("Description-->

    Running this should produce the following:

    Solution Output

Why this is cool

Creating the cars and their options separate from each other makes them very reusable. Any option can be used to decorate any car so long as the specific car implements Car or ICar and the specific car option implements CarOption. Because each car and each option are encapsulated in their own classes, there is no danger of breaking other cars or options when modifications to a specific car or option is needed. For the same reason, we can add new cars and options without fear of breaking existing implementations. Options already defined can be easily reused for new cars that are created. Going the other way, if new options are defined for existing cars, there is no need to modify the code for the car itself. Decorating a car with options in this manner is very easy, and creates very clean, readable, maintainable code. All you have to do is create an instance of the car you want to decorate and pass its instance to the constructor of any option you want to decorate the car with.

History

  • Jan-2-2006 -- Initial creation.

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

About the Author

Eric Ray
Web Developer
United States United States
Currently, Eric lives in Birmingham, Alabama where he works for EBSCO Industries as Web Developer/Analyst

Comments and Discussions

 
SuggestionAnother approach to be more comprehensive PinmemberKenneth Choe25-Sep-12 4:57 
GeneralVery well explained Pinmembernipunasilva22-Aug-12 6:16 
GeneralDecorator using Order Manager as an example PinmemberNilesh Gule4-Jul-12 6:19 
Questionmpi Pinmemberalialhamdi24-Jun-12 10:03 
GeneralMy vote of 5 Pinmemberzzfima3-May-11 22:51 
GeneralMy vote of 5 PinmemberKraftwurm23-Feb-11 6:44 
GeneralMy vote of 1 Pinmembertrojak17-Jan-11 20:17 
GeneralRe: My vote of 1 Pinmemberzzfima3-May-11 22:52 
QuestionCarOption class really required? Pinmemberpvsunil27-Dec-07 19:41 
AnswerRe: CarOption class really required? Pinmembersschleicher1234567896-Dec-10 12:53 
GeneralVB.NET Version PinmemberWarren laFrance24-Sep-07 17:12 
GeneralThinking of "CarWithOption" instead of "CarOption" made it clear for me =) PinmemberPetziWul18-Jan-06 20:55 
QuestionGood article but needs to explain WHY? Pinmembercryod17-Jan-06 11:35 
AnswerRe: Good article but needs to explain WHY? PinmemberTom Clement18-Jan-06 9:15 
GeneralNot Clear PinmemberCharl10-Jan-06 23:27 
GeneralUML Diagrams PinmemberSereger10-Jan-06 22:07 
AnswerRe: UML Diagrams Pinmemberjuanpablobg11-Jan-06 3:36 
GeneralRe: UML Diagrams PinmemberEric Ray13-Jan-06 21:03 
AnswerThe example code can be refactored like this PinmemberLiu Junfeng10-Jan-06 17:01 
GeneralRe: The example code can be refactored like this PinmemberOitz14-Sep-07 3:32 
GeneralConceptually confusing to me PinmemberTom Clement10-Jan-06 15:43 
AnswerRe: Conceptually confusing to me PinmemberEric Ray13-Jan-06 21:00 
GeneralRe: Conceptually confusing to me PinmemberTom Clement14-Jan-06 7:20 
GeneralGreat Start! PinmemberM13134-Jan-06 9:47 
QuestionI dunno... Pinmemberleppie3-Jan-06 7:09 
AnswerRe: I dunno... PinmemberLimeyRedneck3-Jan-06 8:38 
GeneralRe: I dunno... PinmemberEric Ray3-Jan-06 9:09 
GeneralRe: I dunno... PinmemberStillCarel3-Jan-06 10:12 
GeneralRe: I dunno... PinmemberVertyg03-Jan-06 11:06 
QuestionRe: I dunno... Pinmembersoguy26-Feb-07 10:40 

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
Web02 | 2.8.140721.1 | Last Updated 3 Jan 2006
Article Copyright 2006 by Eric Ray
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid