In my previous article, I talked about creational patterns such as the Builder Design Pattern, the Prototype Design Pattern, and the different types of Factory Patterns. Now, let's talk about Structural design patterns.

Structural design pattern helps assemble classes and identify their relationship to make a modular and flexible structure. In this article, I will talk about the Decorator Pattern.

Decorator Pattern

The Decorator Pattern is useful for extending an object's new features while keeping old ones intact. Since this pattern deals with creating modular and flexible structures, we can mix and match various features to create new unique items.

Advantages

So, how is it different from classic inheritance? To understand the difference, let's talk about how a problem can be solved in a classical way and by a decorator pattern.

Suppose we want an already present class to have a completely new feature. We might make an abstract class and other concrete classes that extend from it. Each extended concrete class is different with a unique feature. Let's say we want to mix different features to create new unique ones.

Classically, we will create another new concrete class that derives from our abstract class and implement the feature from other classes. If we want another feature to be merged, we have to create another class. This continues infinitely, where we create new classes for every new feature to mix.

On the other hand, using a decorator pattern allows mixing already present features to dynamically create new features without creating separate classes. Decorator patterns reduce the creation of multiple subclasses while also providing the mixed feature of different objects. This provides inheritance with more flexibility. It also simplifies code as we add new features separately by extending available classes.

Design Diagram

The design diagram for the decorator pattern is given below.

Decorator Pattern Design Diagram

Implementation and Code

Now let's see how we can implement it in our code.

First of all, I create an interface ICharacter and a class Armour. ICharacter has Armour that contains different stats like defense, agility, and strength.

namespace DesignPattern.Patterns.DecoratorPattern
{
    public interface ICharacter
    {
        Armour GetStats();
        
        string GetDescription();

        string GetEquippedItems();
    }

    public class Armour
    {
        public Armour(float defense, float agility, float strength)
        {
            this.defense = defense;
            this.agility = agility;
            this.strength = strength;
        }
        
        public static Armour operator +(Armour armour1 ,Armour armour2)
        {
            var armour = new Armour(0, 0, 0)
            {
                agility = armour1.agility + armour2.agility,
                defense = armour1.defense + armour2.defense,
                strength = armour1.strength + armour2.strength
            };
            return armour;
        }

        public float defense;
        public float agility;
        public float strength;
    }
}
Armour Class and Character Interface

After this, I have created a BasicCharacter which implements ICharacter . This is the base class with the default armour.

/////////////////// Basic Character ////////////////////////
using System;

namespace DesignPattern.Patterns.DecoratorPattern
{
    public class BasicCharacter : ICharacter
    {
        protected Armour myArmour;
        public BasicCharacter(Armour armour)
        {
            myArmour = armour;
            Console.WriteLine("Basic Character Created");
        }
        
        public Armour GetStats()
        {
            return myArmour;
        }
        
        public string GetDescription()
        {
            return $"Equipped : Nothing \n" +
                   $"{ToString()}";
        }

        public override string ToString()
        {
            return $"Agility : {GetStats().agility}\n" +
                   $"Defense : {GetStats().defense}\n" +
                   $"Strength : {GetStats().strength}\n";
        }

        public string GetEquippedItems()
        {
            return "";
        }
    }
}
Basic Character Code

A decorator pattern is about creating a decorator that inherits from the base class or interface. I have implemented ICharacter using this process below. This is called BaseArmour. All decorators should now implement from BaseArmour.

//////////////////////// Base Armour ////////////////

namespace DesignPattern.Patterns.DecoratorPattern
{
    public class BaseArmour : ICharacter
    {
        protected ICharacter character;
        public BaseArmour(ICharacter character)
        {
            this.character = character;
        }
        public virtual Armour GetStats()
        {
            return character.GetStats();
        }

        public virtual string GetDescription()
        {
            return $"Equipped : {GetEquippedItems()} \n" +
                   $"{ToString()}";
        }

        public override string ToString()
        {
            return $"Agility : {GetStats().agility}\n" +
                $"Defense : {GetStats().defense}\n" +
                $"Strength : {GetStats().strength}\n";
        }

        public virtual string GetEquippedItems()
        {
            return character.GetEquippedItems();
        }
    }
}
BaseArmour Decorator

I have created three different decorators, ChestArmour, HeadArmour, and LegArmour, which all derive from BaseArmour. Each class has a unique armour property.


///////////////// chest /////////////////////
namespace DesignPattern.Patterns.DecoratorPattern
{
    public class ChestArmour : BaseArmour
    {
        private Armour _armour;
        public ChestArmour(ICharacter character) : base(character)
        {
            _armour = new Armour(25, -10, 15);

        }
        
        public override Armour GetStats()
        {
            var baseStats = base.GetStats();
            return baseStats + _armour;
        }
        
        public override string GetDescription()
        {
            return $"Equipped : {GetEquippedItems()} \n" +
                   $"{ToString()}";
        }
        
        public override string GetEquippedItems()
        {
            return base.GetEquippedItems() + "Chest, ";
        }
    }
}

/////////////////////////// Head /////////////////////////////

namespace DesignPattern.Patterns.DecoratorPattern
{
    public class HeadArmour : BaseArmour
    {
        private Armour _armour;
        public HeadArmour(ICharacter character) : base(character)
        {
            _armour = new Armour(10,-2,5);

        }
        public override Armour GetStats()
        {
            return base.GetStats() + _armour;
        }
        
        public override string GetDescription()
        {
            return $"Equipped : {GetEquippedItems()} \n" +
                   $"{ToString()}";
        }

        public override string GetEquippedItems()
        { 
            return base.GetEquippedItems() + "Head, ";
        }
    }
}

/////////////// leg /////////////////////

namespace DesignPattern.Patterns.DecoratorPattern
{
    public class LegArmour : BaseArmour
    {
        private Armour _armour;
        public LegArmour(ICharacter character) : base(character)
        {
            _armour = new Armour(4, 6, 12);
        }
        public override Armour GetStats()
        {
            return base.GetStats() + _armour;
        }
        
        public override string GetDescription()
        {
            return $"Equipped : {GetEquippedItems()} \n" +
                   $"{ToString()}";
        }

        public override string GetEquippedItems()
        { 
            return base.GetEquippedItems() + "Leg, ";
        }
    }
}
Different Decorators

Finally, I have combined various properties to create a new unique object in the program below. I have created an object that combines the chest and head to get the combined armour effect. Following this rule, we can mix and match any unique armour to create a character with the combined armour effect.

/////////////////////////// main program /////////////////

using System;
using DesignPattern.Patterns.DecoratorPattern;

namespace DesignPattern
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            ICharacter basicCharacter = new BasicCharacter(new Armour(40, 100, 40));
            Console.WriteLine(basicCharacter.GetDescription());

            ICharacter characterWithChestArmour = new ChestArmour(basicCharacter);
            Console.WriteLine(characterWithChestArmour.GetDescription());
            
            ICharacter characterWithHeadarmour = new HeadArmour(basicCharacter);
            Console.WriteLine(characterWithHeadarmour.GetDescription());
            
            ICharacter characterWithHeadAndChestArmour = new HeadArmour(characterWithChestArmour);
            Console.WriteLine(characterWithHeadAndChestArmour.GetDescription());
            
            ICharacter characterWithHeadChestAndLeg = new LegArmour(characterWithHeadAndChestArmour);
            Console.WriteLine(characterWithHeadChestAndLeg.GetDescription());
        }
    }
}

//////////////////////// output ///////////////

Basic Character Created
Equipped : Nothing
Agility : 100
Defense : 40
Strength : 40

Equipped : Chest,
Agility : 90
Defense : 65
Strength : 55

Equipped : Head,
Agility : 98
Defense : 50
Strength : 45

Equipped : Chest, Head,
Agility : 88
Defense : 75
Strength : 60

Equipped : Chest, Head, Leg,
Agility : 94
Defense : 79
Strength : 72
Implemented Code

From the example above, we can see how powerful the decorator pattern is if applied properly.

Thats it! I hope you learned something new from this. I will write more about different design patterns soon.