Decorator pattern for object composition

Daniil Slobodeniuk
Towards Dev
Published in
5 min readMar 11, 2022

--

Nice to meet you again! Hope you didn’t miss my 2 previous posts about Strategy & Observer patterns👻:

  1. Strategy: https://medium.com/towardsdev/strategy-pattern-for-independent-algorithms-kotlin-70ed24c7bd8b
  2. Observer: https://towardsdev.com/observer-pattern-for-loose-coupling-kotlin-f5ab804609bb

Be sure to read them to get the better understanding of how this series unleashes🤟 As before, for super-duper detailed explanation buy the book: https://www.oreilly.com/library/view/head-first-design/9781492077992/

Structure:

  • Intro
  • Problem
  • Design principles to follow
  • Final solution code (with additional theory on this pattern as it’s pretty blurry)
  • Drawing

Let’s dive asap to the gist!

Decorator pattern

Intro

When you develop something in a haste, it’s no wonder that such a work will bring lots of underlying issues👎🏻 The most insidious of them is bad design which makes it pretty arduous to resolve all the mess around (you’ll see an example in the Problem section).

Solution? Ah…😪 There is no spell that magically untangles mess in a moment. However🖐 Being cognizant of design patterns and OOP principles will save you time and future hustle as you’ll roughly compare your business task implementation with those 2 mentioned things and decide: how to make my software not simply working, but extensible and manageable in the future🛠 Yes, it sounds vague, but with experience you’ll spot such points faster.

Although, it doesn’t mean that you shouldn’t create temporary version to get the idea of how future-to-be-product should look like

Problem

So, you have a base class that is extended by many other classes. For simplicity, let’s imagine:

  • base class is Beverage
  • children are types of that Beverage

So, at some moment you may think it’s even okay. But what if we have a cafe with tens, hundreds of Beverage s? And the main difference is ingredients❓

  • So we need to add new similar classes
  • What if implementation of cost() will change or new method appear?

Recall when I said about bad design in the Intro section

Then you may think: put ingredients variables inside parent class and scatter getters & setters (Java version || some methods in Kotlin/other language). At first, let’s observe this “modified” solution:

Problems here? Let’s enumerate them:

  1. What if price for ingredient will change? We’ll need to change existing code => bad!
  2. New ingredients will force us again to alter code
  3. All children of Beverage will inherit those checks for ingredients, but what if we don’t need them?

etc…❗️

Design principles

We all have heard about SOLID (I hope😰). What does “O” mean?

  1. Open-Closed principle: our objects/classes should be open for extension, but closed for modification.

Yeah yeah, it sounds like vice versa stuff. I thought that way when firstly heard about it, but let me tell you the real meaning:

Our class mustn’t allow others to be modified: for example some methods or properties should stay as the same, however, our class:

  1. can be extended without making change to the initial code. How? Recall Observer pattern (link at the beginning) where we extend Subject with concrete observer. We leveraged methods from Subject and further can change results from that class without making changes to the Subject class itself
  2. We can specifically make some methods open, others private/protected

You may question yourself: how can I engineer systems which will follow this rule🧐? Answer:

  • with experience
  • you shouldn't do it as new levels of abstraction adds complexity => concentrate on the chunks which are likely to be under alteration and apply the principle there
  • use already created patterns to ease up your development

Final solution code

Following the link below, you can look at my code which I’ll explain further and match with pattern theory

At first, let me introduce another usage of inheritance🙌: inheritance for type matching rather than simple behavior inheriting. How does it work?

  • Child IS-A Parent, but doesn't bluntly use methods. Actually, it’s for type matching, i.e. making this class of parent. WHY USE SUCH A GIMMICK??

Let me draw a diagram of the Decorator pattern:

                     Main Component
/ \
Concrete Component Main Decorator
/ \
Concrete Decorator 1 Concrete Decorator 2

Here Main Decorator needs to be of the same type as Concrete Component as former will decorate latter. Hence, we use inheritance for type matching.

How does it look in a more picture-like version?

                 DarkCoffee
decorated by Mocha
decorated by Whip
etc

It’s like a snowball. In the center we have our Concrete Component (child of Main class, i.e. Beverage) , and then there are Decorators added like new layers.

Info about those decorators:

  1. They are of same super-type as object they decorate
  2. There can be multiple decorators applied
  3. We can pass already decorated object (and actually we do it)
  4. We can decorate objects at runtime
  5. Decorators can add it’s own behavior before/after decorating the object

In my example we won’t go too deep into new methods and stuff like that to lessen the mental burden.

We acquire new behavior not by inheriting from super-type/class, but by composing objects together

Let’s analyze the code:

  1. mainComponent.kt is our abstract class which gives birth to everything
  2. concreteComponent.kt is a child which will be decorated
  3. mainDecorator.kt is abstract class for decorators. It inherits for type which was discussed above
  4. concreteDecorator.kt & concretteDecorator2.kt are examples of decorators which will be applied on concreteComponent

How does everything work?

  • look at main.kt : we initialize new component and wrap it into 2 decorators. When we call olivesPizza.cost() the chain is triggered:
  1. we enter concreteDecorator.kt cost()which in turn calls it’s currentPizza .
  2. This currentPizza is concreteDecorator2.kt : Cheese. It calls it’s currentPizza which is FreshPizza aka concrete component.
  3. This concrete component returns 25. Then this 25 + 5 returns from Cheese aka concreteDecorator2.kt to concreteDecorator.kt where final 4 is added🙌

Same applies to currentDescription() method. Can you untangle it? If something is obscure, let me know in the comments!

Drawing✍🏻

Here you can observe the explained above schema in flesh. On the left side there is a general blueprint, whilst on the right side there is an example of code in my GitHub repo.

Drop message in comment if you wanted me to elucidate it

IS-A means inheritance. But don’t forget about inheritance for type matching, not simple behavior inheritance☝🏼

Outro😪

Academic definition of Decorator pattern: adds extra responsibilities to an object dynamically. It provides ability to extend functionality without subclassing over-usage

I know that this pattern is a little bit more nebulous compared to the previous, but hope you’ve got the crux👋

You can find me:

--

--

Zealous lad who is a self-taught SWE👨‍💻, Japan-obsessed サムライ🎌and the one who is never tired || afraid of trying something new⚡️. Subscribe! よろしお願いいたします✌️