Design Patterns - Decorator

06 mei 2022 om 10:00 by ParTech Media - Post a comment

In our previous blog of the design pattern series, we had discussed the Composite design pattern, which falls under the category of structural design patterns. Today, we will explore another design pattern from the same category called Decorator Design Pattern.

Table of contents

  1. What is Decorator Design Pattern?
  2. Implementation of Decorator Design Pattern
  3. Conclusion

Decorator Design Pattern

We often create classes with methods and properties inside them to achieve certain functionality. A few times, the created classes will be sufficient to handle the necessary requirements. But on some occasions, there might be a need to perform one or more actions in addition to what is already done. Using inheritance and subclassing is a good way to handle such a situation. But what about the cases where subclassing is not possible (the parent class is sealed), or say you want to make changes more flexibly by extending functionalities dynamically?

This is achieved by using a Decorator Pattern. This essentially involves wrapping the original class with the extended functionality, which is then exposed to end users for consumption. The decorator pattern is also referred to as a Wrapper pattern. Decorator patterns come in handy when the class is sealed and when there is a need to provide an alternate for sub-classing.

Implementation of Decorator Design Pattern

The decorator design pattern mainly consists of

  1. Component
  2. Concrete Component
  3. Decorator
  4. Concrete Decorator

Component is the interface and also the base (or the actual object) for which the additional functionalities need to be added.

Concrete component is the implementation of the Component interface, which also gets benefitted from the added functionality.

Decorator provides the declaration for all the extended functionalities that need to be added to the concrete component.

Concrete Decorator provides the definition for the declarations for the decorator.

The below code is the template for implementing the Decorator pattern.

using System;

namespace PARTECH_Decorator
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IComponent component = new Component();

            ConcreteDecorator decorator = new ConcreteDecorator(component);

            decorator.Action();

            Console.ReadLine();
        }
    }

    public interface IComponent
    {
        void Action();
    }

    public class Component : IComponent
    {
        public void Action()
        {
            Console.WriteLine("Component - Action");
        }
    }

    public abstract class Decorator : IComponent
    {
        private IComponent _component;

        public Decorator(IComponent component)
        {
            _component = component;
        }

        public virtual void Action()
        {
            _component.Action();
        }
    }

    public class ConcreteDecorator: Decorator
    {
        public ConcreteDecorator(IComponent component) : base(component)
        {

        }

        public override void Action()
        {
            base.Action();
            Console.WriteLine("Action can be overriden here.");
        }

    }
}

Here,

  • IComponent (Component) is an interface that has the method signature.
  • ConcreteComponent is a class that implements the interface IComponent and provides implementations for the methods in the IComponent.
  • Decorator is an abstract class that also implements the IComponent. In this, the functionalities that need to be extended are made virtual to override in the child class.
  • ConcreteDecorator implements the Decorator class, which takes up concretecomponent as input and helps in extending its functionality.

In the main method, an object of concretecomponent and an object of concretedecorator are created by passing the object concretecomponent, which calls the action method.

On executing the above code, the below output gets printed -

Let’s implement the Decorator design pattern in a real-life scenario and understand it better.

Consider a mobile phone store which sells all the latest mobile phones. These phones come with a lot of accessories. Now, the store has a billing software which we are going to mimic through the below code -

using System;

namespace PARTECH_Decorator
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IMobile mobile = new ModelB();

            MobileDecorator decorator = new HardcaseAccessory(mobile);

            Console.WriteLine(decorator.GetType());
            Console.WriteLine(decorator.GetPrice());

            Console.ReadLine();
        }
    }

    public interface IMobile
    {
        string GetType();
        double GetPrice();
    }

    public class ModelA : IMobile
    {
        public string GetType()
        {
            return "Model A Mobile phone";
        }

        public double GetPrice()
        {
            return 500;
        }
    }

    public class ModelB : IMobile
    {
        public string GetType()
        {
            return "Model B Mobile phone";
        }

        public double GetPrice()
        {
            return 450;
        }
    }

    public class ModelC : IMobile
    {
        public string GetType()
        {
            return "Model B Mobile phone";
        }

        public double GetPrice()
        {
            return 550;
        }
    }

    public abstract class MobileDecorator : IMobile
    {
        private IMobile _mobile;

        public MobileDecorator(IMobile mobile)
        {
            _mobile = mobile;
        }

        public virtual string GetType()
        {
            return _mobile.GetType();
        }

        public virtual double GetPrice()
        {
            return _mobile.GetPrice();
        }
    }

    public class PowerbankAccessory: MobileDecorator
    {
        private IMobile _mobile;
        public PowerbankAccessory(IMobile mobile) : base(mobile)
        {

        }
        public override string GetType()
        {
            return base.GetType() + " and power bank";
        }

        public override double GetPrice()
        {
            return base.GetPrice() + 15;
        }
    }

    public class HardcaseAccessory : MobileDecorator
    {
        private IMobile _mobile;
        public HardcaseAccessory(IMobile mobile) : base(mobile)
        {

        }
        public override string GetType()
        {
            return base.GetType() + " and hard case";
        }

        public override double GetPrice()
        {
            return base.GetPrice() + 5;
        }
    }
}

In the above code,

  • IMobile is the component and has the definition for two methods, GetType and GetPrice.
  • MobileA, MobileB, MobileC are the Concrete components that implement IMobile and have their own price and type details.
  • MobileDecorator abstract class is the decorator, which again implements IMobile. It provides virtual methods for GetType and GetPrice, which can be overridden in the child classes.
  • Powerbankaccessory and Hardcaseaccessory are concrete decorators which have the code to implement the pricing logic of the respective accessory along with the price of the mobile that is chosen.

In the main method, an object of IMobile viz. MobileB is chosen and for the mobile decorator, hardcase ischosen. For the hardcase class, the object of mobile is passed. Then the Get price and Get type methods are called. Finally, the below result gets displayed on executing the code -

If you notice, MobileB class GetType method holds ‘Model B Mobile phone’ and hard case accessory class appends ‘and hard case’ along with it. That is achieved by calling the base class method, which is mobile b here.

Similarly, if you notice in the mobile b class, the price has been mentioned as 450. In the hard case accessory class, the base class price is added by 5 to provide the final price of 455.

Conclusion

And that’s what a Decorator Structural design pattern is all about. In this post, we have also seen how to implement it in a real-world scenario. Stay tuned for the next blog on the Design Patterns series.