Design Patterns – Decorator

By | 11/10/2023

In this post, we will see what the Decorator Pattern is and how we can implement it.
But first of all, what is the Decorator Patterns?
From Wikipedia:
In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified.
Decorator use can be more efficient than subclassing, because an object’s behaviour can be augmented without defining an entirely new object.

In a nutshell, the Decorator Pattern provides a flexible alternative to subclassing for extending functionality. By using decorators, we can add responsibilities to objects incrementally and dynamically.

To understand the Decorator Pattern better, let’s break it down into its main components:
Component: This is the abstract class/interface that defines an object to which additional responsibilities can be attached.
Concrete Component: This is a concrete implementation of the Component. It’s the object to which we’ll add new behaviour.
Decorator: This is the abstract class that extends the Component and has a reference to a Component. It wraps the Concrete Component.
Concrete Decorator: These are concrete implementations of the Decorator, which add additional behaviour.

Let’s see an example:
Suppose we have a basic Book class. A book can have a title and a base cost. Now, consider that there are multiple “extras” we can add to a book, such as an autograph by the author or we could sell this book second hand. Instead of creating separate subclasses for each combination of book and its extras, we’ll use the Decorator Pattern.

COMPONENT (Book.cs):

namespace DecoratorPattern;

internal abstract class Book
    public abstract string Description { get; }
    public abstract double Cost();


namespace DecoratorPattern;

internal class Novel: Book
    public override string Description => "Novel";
    public override double Cost() => 10.0;

DECORATOR [BookDecorator.cs]:

namespace DecoratorPattern;

internal abstract class BookDecorator : Book
    protected Book _book;

    public BookDecorator(Book book)
        _book = book;

CONCRETE DECORATOR 1 [BookWithAutograph.cs]:

namespace DecoratorPattern;

internal class BookWithAutograph : BookDecorator
    public BookWithAutograph(Book book) : base(book) { }

    public override string Description => _book.Description + " with autograph";
    public override double Cost() => _book.Cost() + 5;

CONCRETE DECORATOR 2 [BookSecondHand.cs]:

namespace DecoratorPattern;

internal class BookSecondHand:BookDecorator
    public BookSecondHand(Book book) : base(book) { }

    public override string Description => _book.Description + " second hand";
    public override double Cost() => _book.Cost() - (_book.Cost() * 0.10);

Finally, we define the Program.cs file that we will use to implement these classes:

using DecoratorPattern;

Book objNovel = new Novel();
Console.WriteLine($"Description: {objNovel.Description}  -  Price:{objNovel.Cost()}");

Book objNovelWithAutograph = new BookWithAutograph(objNovel);
Console.WriteLine("Novel with autograph");
Console.WriteLine($"Description: {objNovelWithAutograph.Description}  -  Price:{objNovelWithAutograph.Cost()}");

Book objNovelSecondHand = new BookSecondHand(objNovel);
Console.WriteLine("Novel second hand");
Console.WriteLine($"Description: {objNovelSecondHand.Description}  -  Price:{objNovelSecondHand.Cost()}");

We have done and now, if we run the application, the following will be the result:

Leave a Reply

Your email address will not be published. Required fields are marked *