Design Patterns – Decorator

Decorator is a design pattern used to dynamically „decorate” (e.g. add new actions) another classes. Let’s say that you have class which describes Window. It can be opened or closed. Then, you decide to decorate it with curtains or to clean it – you  will do it yourself or hire somebody, who will be a decorator.

Let’s create a simple project:

1

1) First of all, you need to add „Decorator” folder. Inside of this folder please create Window.cs class which will describe our window in the real world.

Window.cs:

using System;

namespace MyDesignPatterns.Decorator
{
    internal class Window
    {
        public void OpenWindow()
        {
            Console.WriteLine("Window is opened.\n");
        }

        public void CloseWindow()
        {
            Console.WriteLine("Window is closed.\n");
        }
    }
}

Now we need to add a decorator for it which cleans and decorates our window object. Name it WindowDecorator.cs:

using System;

namespace MyDesignPatterns.Decorator
{
    internal class WindowDecorator
    {
        private readonly Window newWindow;

        public WindowDecorator(Window newWindow)
        {
            this.newWindow = newWindow;
        }

        private void CleanWindow()
        {
            Console.WriteLine("Window is clear.\n");
        }

        private void DecorateWindow()
        {
            Console.WriteLine("Window is decorated.\n");
        }

        public void TakeAction()
        {
            //Methods from WindowDecorator.cs
            this.CleanWindow();
            this.DecorateWindow();

            //Methods from Window.cs
            newWindow.OpenWindow();
            newWindow.CloseWindow();
        }

    }
}

As you can see, the action is taken both for Window object and WindowDecorator.

Program.cs to run it:

using System;

namespace MyDesignPatterns
{
    using MyDesignPatterns.Decorator;

    internal class Program
    {
        private static void Main(string[] args)
        {
            WindowDecorator newWindowDecorator = new WindowDecorator(new Window());
            newWindowDecorator.TakeAction();
            Console.ReadKey();
        }
    }
}

New instance of WindowDecorator is created with Window passed as argument. Then, TakeAction() method is invoked. That’s all 🙂

EDIT:

As @tkestowicz proposed, this example should be designed with abstraction (I have already used interfaces in previous patterns, where these are optional – in Decorator it is necessary). If there is a situation that we should add another part of equipment to code we will have a big mishmash

Lets’s modify and add IEquipment.cs which will be implemented by our base classes:

IEquipment.cs:

namespace MyDesignPatterns.Decorator
{
    interface IEquipment
    {
        void Open();

        void Close();
    }
}

Window.cs:

using System;

namespace MyDesignPatterns.Decorator
{
    internal class Window : IEquipment
    {
        #region IEquipment Members

        public void Open()
        {
            Console.WriteLine("Window is opened.\n");
        }

        public void Close()
        {
            Console.WriteLine("Window is closed.\n");
        }

        #endregion
    }
}

I have also added Door.cs to show how it works:

using System;

namespace MyDesignPatterns.Decorator
{
    class Door : IEquipment
    {

        #region IEquipment Members

        public void Open()
        {
            Console.WriteLine("The door is opened.\n");
        }

        public void Close()
        {
            Console.WriteLine("The door is closed.\n");
        }

        #endregion
    }
}

EquipmentDecorator.cs instead of WindowDecorator.cs:

using System;

namespace MyDesignPatterns.Decorator
{
    internal class EquipmentDecorator
    {
        private readonly IEquipment newEquipment;

        public EquipmentDecorator(IEquipment newEquipment)
        {
            this.newEquipment = newEquipment;
        }

        private void CleanEquipment()
        {
            Console.WriteLine("Equipment is clear.\n");
        }

        private void DecorateEquipment()
        {
            Console.WriteLine("Equipment is decorated.\n");
        }

        public void TakeEquipmentAction()
        {
            //Methods from EquipmentDecorator.cs
            this.CleanEquipment();
            this.DecorateEquipment();
     
            newEquipment.Open();
            newEquipment.Close();
        }

    }
}

And Program.cs to invoke:

using System;

namespace MyDesignPatterns
{
    using MyDesignPatterns.Decorator;

    internal class Program
    {
        private static void Main(string[] args)
        {
            //Decorator

            EquipmentDecorator newDecorator = new EquipmentDecorator(new Window());
            newDecorator.TakeEquipmentAction();

            newDecorator = new EquipmentDecorator(new Door());
            newDecorator.TakeEquipmentAction();

            Console.ReadKey();
        }
    }
}

5 uwag do wpisu “Design Patterns – Decorator

  1. I think you should use an abstraction even if it is just a simple example. For me one of the biggest benefits of using Decorator Pattern is that you can use base object without figuring out which decorators are applied. Without it you lose flexibility because e. g. when you create another decorator you will have to add new constructor which can deal with new type. Besides if you decide to switch decorator then you will have to change type in all places you use decored object.

    1. Yes, of course – in real project you will use interface for Window, Let’s say that window implements interface like IEquipment. You are right that in this simple example I should add this.Thanks for comment, I will add example with interfaces.

  2. IMHO you should also add abstraction on EquipmentDecorator class.
    What if You want to add another feature? For example I want to clean the window, decorete it and then break the window. How can I do that?

    1. This is just a simple example of how you can achieve the goal with a decorator design pattern. Of course, if we think about the enterprise project it is obvious that there should be abstraction for EquipmentDecorator. Moreover, we should also think about dependency injection with Ninject – more about this will be in the next article about IoC and Dependency Injection:)

  3. internal class EquipmentDecorator: IEquipment
    That’s how it should be done – with this you are able to add new functionality to base object methods. Abstraction on EquipmentDecorator is also required – without this we cannot say that it is real decorator example. Btw TakeEquipmentAction should be IEquipment method – without this you will write code against EquipmentDecorator (and in real decorator pattern your code should use IEquipment – so it wont know nothing about TakeEquipmentAction method).

Zostaw komentarz

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

w

Connecting to %s