Hey there, my last posts looked at creational and behavioral design patterns. This time we're going to be going over our first structural pattern, the Decorator pattern. Structural patterns give us a way of combining objects into larger structures while keeping these structures flexible and efficient. When you think of the Decorator pattern, think of Matryoshka dolls (the dolls that stack inside of one another). You'll soon see that the Decorator pattern gives our code flexibility at run-time while extending class functionality.
If you don't know what a wrapper class is, you might want to check out this post first! (But it's not required)
Overview
Definition
Decorate: make (something) look more attractive by adding extra items or images to it. The Decorator pattern allows us to add functionality to our classes without directly modifying or inheriting from them. We're able to add behavior to our classes by wrapping them up inside of another class.
We can dynamically add responsibilities to objects with a Component and Decorator relationship. The Decorator conforms to the Component's interface while also having an instance Component object within itself. The Decorator object is then effectively able to add functionality to the Component by adding functionality to itself.
Implementation
To achieve the Observer pattern, there are 4 types of classes we need to implement:
- Component
- Defines the interface for the Concrete Component we want to dynamically add to.
- Concrete Component
- The class we are adding functionality to.
- Decorator
- Maintains reference to the Concrete Component we're adding to.
- Implements Component's interface (by wrapping the Component's methods).
- Concrete Decorator
- Adds the functionality.
Using C#, the pattern looks like this:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
ConcreteComponent component = new ConcreteComponent();
component.DoSomething();
ConcreteDecorator decorator = new ConcreteDecorator(new ConcreteComponent());
decorator.DoSomethingElse();
}
// 2. The concrete component (what we're decorating)
class ConcreteComponent : IComponent
{
public void DoSomething()
{
Console.WriteLine("Doing something!");
}
}
// 1. The component's interface
interface IComponent
{
void DoSomething();
}
// 3. The decorator
abstract class Decorator : IComponent
{
protected IComponent Component { get; set; }
public Decorator(IComponent component)
{
this.Component = component;
}
public void DoSomething()
{
this.Component.DoSomething();
}
}
// 4. The concrete decorator
class ConcreteDecorator : Decorator
{
public ConcreteDecorator(IComponent component) : base(component) {}
public void DoSomethingElse()
{
Console.WriteLine("Doing something else!");
}
}
}
Output:
Doing something!
Doing something else!
You can play with this code here on .NET Fiddle
Example Code
Decorators are good to use when you don't want to/can't change the behavior of a class. You can run into this situation for a lot of different reasons, you might be working with legacy code that shouldn't be modified, or working with a library that you cannot modify.
Let's write a program that adds functionality to a Calculator class. Our Calculator class only has an Add method right now, but we want to add a Subtract method so we'll create a decorator for the class.
using System;
public class Program
{
public static void Main()
{
Calculator calculator = new Calculator();
Console.WriteLine("Regular Calculator:");
Console.WriteLine(calculator.Add(4, 3) + "\n");
BetterCalculator betterCalculator = new BetterCalculator(calculator);
Console.WriteLine("Decorated Calculator:");
Console.WriteLine(betterCalculator.Add(4, 3));
Console.WriteLine("But I can also subtract!");
Console.WriteLine(betterCalculator.Subtract(4, 3));
}
class Calculator : ICalculator
{
public override int Add(int x, int y)
{
return x + y;
}
}
abstract class ICalculator
{
public abstract int Add(int x, int y);
}
class Decorator : ICalculator
{
ICalculator myCalc { get; set; }
public Decorator(ICalculator calculator)
{
this.myCalc = calculator;
}
public override int Add(int x, int y)
{
return this.myCalc.Add(x, y);
}
}
class BetterCalculator : Decorator
{
public BetterCalculator(ICalculator calc) : base(calc) {}
public int Subtract(int x, int y)
{
return x - y;
}
}
}
Output:
Regular Calculator:
7
Decorated Calculator:
7
But I can also subtract!
1
You can play with this code here on .NET Fiddle
Top comments (0)