Decorator -> Structural Arrogant Design Pattern

Arrogant Design Patterns ( by Hafiz Waleed Hussain )

Hi, I hope everyone is doing good.

Today, we are going to discuss how to be a magician in coding :). Means we will address one of the Structural Design Pattern. In my opinion, if someone is too good with Structural Design Patterns, he/she can impress you easily by making some small changes at the abstract level. Magic will be your whole application will start working with new requirements, but without making any changes in your entire application.

In case you missed the Introductory Post of Arrogant Design Patterns.
More Posts on Design Patterns https://www.uwanttolearn.com/design-patterns/

For the official or popular definition of Structural Design Pattern, you can consult with Google. I will share my thoughts with you, and maybe you disagree with that, which is okay.

For me, Structural Patterns are something like Data Structure. All languages have Primitive Data Types like int, float, boolean. Now, we can design our Data Structure by using these Primitive types, as shown below in a Pseudo Code.

struct MyDataStructure{
public int age;
public float height;
public boolean isAdult;
}

What did we do in the above code? I took Primitive Data Types and combine them in a new class or structure by doing Composition ( Will discuss composition after this ). So I created a new Structure by using current Primitive Data Types or Structures, which will be helpful for my System.

Now, this is the same analogy that will work in Structural Design Patterns. Only the difference is, in Structural Design Patterns, we will use interfaces, inheritance, and composition to create new structures (which we will see in all posts of Structural Design Patterns). And in the case of Data Structure example, we used Primitive Data Types to create new Data Structures. I hope things are a little more clear.

Revision:

Note: Revision, I copied from my existing post on Design Patterns. If you already read this, please skip and scroll to Decorator Design Pattern heading. But I will recommend you to revise this if possible that will take only 2-3 extra minutes.

We need to do some concepts revise, which are essential from the perspective of Design Patterns.

I am sure everyone who is reading this blog is aware of how to use

  • if, else if, switch, when
  • for loop, while loop, do-while loop
  • Functions,
  • Classes, Objects
  • Inheritance, Encapsulation, Polymorphism
  • Composition
  • Dependency Injection
  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation
  • Dependency Inversion Principle

Still, I am going to revise some of the concepts :).

Note: I am going to use the kotlin and java syntax, but you can relate these examples with any OOP language.

How to create an Object:

class NameOfAClass{}
val objectOfAClass = NameOfAClass()

In the above code, first I created a class with the name “NameOfAClass” and after that, I created an object from this class.

Inheritance:

open class Parent{}
class Child : Parent(){}

// In Java
public class Parent{ }
public class Child extends Parent{}

In the above code, Parent is a class and Child is also a class but that class also inherits the Parent.

Polymorphism:

I am not going to explain what is Polymorphism, for that you can ask Google. I am going to show you how we can create a Polymorphism scenario.

open class Parent{
open fun doSomething(){
println("Do Something")
}
}

class ChildOne: Parent(){
override fun doSomething(){
super.doSomething()
println("ChildOne Do Something")
}
}

class ChildTwo: Parent(){
override fun doSomething(){
super.doSomething()
println("ChildTwo Do Something")
}
}

fun main() {
var parent: Parent = ChildOne()
parent = ChildTwo()
}

// In Java

public class Parent{ 
    void doSomething(){
        System.out.println("Do Something");
    }
}
public class ChildOne extends Parent{
    void doSomething(){
        super.doSomething();
        System.out.println("ChildOne Do Something");
    }
}

public class ChildTwo extends Parent{
    void doSomething(){
        super.doSomething();
        System.out.println("ChildTwo Do Something");
    }
}

public static void main(String[] args) {
    Parent parent = new ChildOne();
    parent = new ChildTwo()
}

In the above code, we have three classes, Parent, ChildOne and ChildTwo. ChildOne and ChildTwo are both inherit from Parent class. Now, where is polymorphism? We can see Polymorphism in the main method.

public static void main(String[] args) {
    Parent parent = new ChildOne();
    parent.doSomething()
   
    parent = new ChildTwo()
    parent.doSomething()
}

Basically, in this code, everything in front of us, that is why we can see, and we can predict Polymorphism behavior like when a parent.doSomething() called the first time, we will see the output of ChildOne implementation and when the second time parent.doSomething() called we would see the ChildTwo implementation. So the Parent is showing us a Polymorphic behavior. You need to remember this, instead, here I can see a Factory Pattern use case, but for now, if you are not able to see a Factory Pattern use case, it’s Okay. You will see one-day :).

Before going to the next revision topic. I am going to change something in the above main method code so we can see a unpredict Polymorphism behavior.

public static void main(String[] args) {
    Parent parent = getInstanceOfChild();
    parent.doSomething()
}

Now, anyone can tell me what will be the output of parent.doSomething() with your current knowledge? I know, No one can give me the answer until we see the implementation of getInstanceOfChild(). We can say, parent is still showing a Polymorphism, but from the perspective of the main method, I introduce an abstraction. Like you only know the interface of the getInstanceOfChild(), but you are not aware of the implementation of getInstanceOfChild(). 

Composition:

Again, I am not going to explain what is Composition you can consult from Google. Instead, I am going to show you how to achieve Composition in our above inheritance example.

class Parent{
    open fun doSomething(){
        println("Do Something")
    }
}

class ChildOne(val parent: Parent){
    fun doSomething(){
        parent.doSomething()
        println("ChildOne Do Something")
    }
}

class ChildTwo(val parent: Parent){
    fun doSomething(){
        parent.doSomething()
        println("ChildTwo Do Something")
    }
}

fun main() {

    val parent = Parent()
    val childOne = ChildOne(parent)
    val childTwo = ChildTwo(parent)
    
    childOne.doSomething()
    childTwo.doSomething()
}

// In Java 

public class Parent{
    void doSomething(){
        System.out.println("Do Something");
    }
}
public class ChildOne{
    private Parent parent;
    public ChildOne(Parent parent){
        this.parent = parent;
    }

    void doSomething(){
        parent.doSomething();
        System.out.println("ChildOne Do Something");
    }
}

public class ChildTwo{

    private Parent parent;
    public ChildTwo(Parent parent){
        this.parent = parent;
    }

    void doSomething(){
        parent.doSomething();
        System.out.println("ChildTwo Do Something");
    }
}

public static void main(String[] args) {
    Parent parent = new Parent();
    ChildOne childOne = new ChildOne(parent);
    ChildTwo childTwo = new ChildTwo(parent)
}

In the above code, we refactor our inheritance code to composition: Elementary, no rocket science. Try to compare both if you are not feeling comfortable with this code spend some time.
Now, the next questions are what we lose and what we gain when we change to composition?

What did we lose?

  1. Lose Flexibility -> If I want some method or filed available for ChildOne and ChildTwo, I can add that field or method in Parent class, and then I will get that in all of our inherit classes from Parent. 
  2. Polymorphism -> In our main method, I am not able to enjoy polymorphism because ChildOne now did not inherit from Parent. So If I do a try like Parent parent = new ChildOne() that will not compile.
  3. No more abstraction. I am not able to achieve abstraction; instead, I need to write a concrete code.

What did we achieve?

  1. Gain Flexibility -> If I want to add something in Parent that will not impact my ChildOne and ChildTwo classes. So Parent code is the independent, or maybe a more appropriate case will be If I remove something which is not used in ChildOne and ChildTwo there will be no impact on my code. But in the case of Inheritance, mostly removal of code is painful.
  2. Specific functionality -> In case of composition, if I want to add some functionality that is only specific to ChildTwo but not available to ChildOne, I can add easily by giving that new functionality as a constructor parameter to ChildTwo. In the case of inheritance, we mostly need to add a specific method in Parent class to achieve the Liskov Substitution Principle, and then we need to remember don’t call that method in ChildOne but do override in ChildTwo from where mostly we start losing our control on Inheritance code.

As you can see, Inheritance and Composition are compelling concepts. Still, both can make our code amazing, and both can make our life hell, especially Inheritance, if we do design our classes carelessly. But in the case of Composition, we lose a concept of Polymorphism, which is the foundation of scalable code. I will discuss more this in later posts.
In conclusion, we need to use both; instead, some Design Patterns are using both concepts, and they give us a fantastic power like I will show you this power in the Decorator Pattern post.

Interface:

I am not going to explain to you what an interface is for that you can consult with Google. Instead, I am going to use this interface directly in our code.

I am going to refactor our Inheritance example by using an interface.

interface Base{
    fun doSomething()
}

open class Parent: Base{
    override fun doSomething(){
        println("Do Something")
    }
}

class ChildOne: Parent(){
    override fun doSomething(){
        super.doSomething()
        println("ChildOne Do Something")
    }
}

class ChildTwo: Parent(){
    override fun doSomething(){
        super.doSomething()
        println("ChildTwo Do Something")
    }
}

fun main() {
    var base: Base = Parent()
    base.doSomething()

    base = ChildOne()
    base.doSomething()

    base = ChildTwo()
    base.doSomething()
}

// In Java

public interface Base {
    void doSomething();
}

public class Parent implements Base {
    @Override
    public void doSomething() {
        System.out.println("Do Something");
    }
}

public class ChildOne extends Parent {
    public void doSomething() {
        super.doSomething();
        System.out.println("ChildOne Do Something");
    }
}

public class ChildTwo extends Parent {
    public void doSomething() {
        super.doSomething();
        System.out.println("ChildTwo Do Something");
    }
}

public static void main(String[] args) {
    Base base = Parent();
    base.doSomething();

    base= new ChildOne();
    base.doSomething();

    base = new ChildTwo()
    base.doSomething();
}

The above code is the same as the Inheritance one, only we got one more layer of Abstraction, and in the main method, now Base is the one who is showing the Polymorphic behavior. In this example, I am not able to show you a real power of Interface, but trust in me, interfaces are a fantastic concept, and that can make your code scalable.

Instead, I can show you some power of Interface. Take an example. You are a UI Engineer of some SDK. Now you created a Button class. You are also aware of your Button class will be used by thousands of developers.

Now, you are thinking about how to implement the logic of Button Click because you don’t know what you should do when button click. Like one developer wants when a button is click to execute the logic of logout, other developers want to write the execute login logic on button click. Some other developer wants to start a checkout process when button click. Now, this is the one use case of an interface. When you are not aware of the implementation, but you know you can create some abstraction and provide that abstraction layer to your users. Later you can call their concrete implementations by using your given abstraction as we can see in the below example.

interface Click {
    fun onClick()
}

class Button {

    private var clickListener: Click? = null

    fun setClickListener(click: Click) {
        clickListener = click
    }

    fun onDrawOrSomething() {
        // A lot of Code related to Button UI or internal logic
        clickListener?.onClick()
    }
}

So in the above example, Click interface is in abstraction for your code. You don’t know what developers will implement against this, but now they can use it, and your code is independent of there concrete Implementation.

Now, we are ready to move to the Decorator Design Pattern.

Decorator Design Pattern:

Before going to start a discussion about the Decorator Design Pattern, I am sharing with you one crucial point about Structural Design Pattern. These Patterns are not as simple as Creational Design Patterns. If you are new in the OOP or Design Patterns world, you need to do a lot of practice, thinking, observation, and try to observe existing code and ask questions to your self.

  • Why you are using inheritance, are you able to refactor your current code to Composition? ( vice versa also a good question )
  • Why you are using a base class, are you able to create an interface from base class methods and then implement that interface into your base class?
  • Why you are using Concrete classes as reference objects, why not change references to Base or Abstract classes or maybe to interfaces?
    interface Parent{}
    class Child implements Parent{}
    public static void main(String[] args) {
    Child child = new Child();
    // to
    Parent parent = new Child();
    }

Ask these questions and do some refactoring for practice in your spare time. That will be helpful for you. Otherwise, you will face the problem, in the case of the Structural Design Pattern.

Now, we are going to start a discussion on the Decorator Design Pattern. We have two options. First, we will discuss the definition and use cases. Second, we will see how to achieve the Decorator Design Pattern in code, and the first strategy will automatically be part of our discussion. So I am going with the second strategy.

Note My implementation may be a little different from the currently available implementations on Google. Still, I am sure once you will grab this implementation, you can implement this Pattern how you want.

I am going to show you the code with pictures, so your brain easily captures all the details.

I have an Interface.

interface One {
    void one();
}

class OneImpl implements One {

    @Override
    public void one() {
        System.out.println("One Impl");
    }
}

Hey, all Inheritance lovers, this is the crucial step, one mistake you will lose the Decorator Design Pattern :). Now, I am going to take the wrong path here and will come back to the Decorator Design Pattern. We need to add some new functionality once OneImpl one() method completes its execution, so we decided we will do inherit this class and will add our new functionality.

interface One {
    void one();
}

class OneImpl implements One {

    @Override
    public void one() {
        System.out.println("One Impl");
    }
}

class NewFunctionality extends OneImpl{
    
    void newFunctionality(){
        one();
        // Our new functionality
    }
} 

public static void main(String[] args) {
     One newFunctionality = new NewFunctionality();
     newFunctionality.one();
}

I am sure some of you already find out the use case of the Decorator Design Pattern. So if we observe carefully our code, we can see one thing.

void newFunctionality(){
one();
// Our new functionality
}

Our one() method first will complete its execution, and after that, our new functionality will start its execution. In simple words, we did decoration around one() method, but we use inheritance, which is not good to decorate. Instead, we need to use Composition. The Composition is more potent for decoration scenarios and more scalable, which we will see later. So now, I am going to revert my inheritance mistake and going to use Composition so we can say, we implemented Decorator Design Patter.

interface One {
    void one();
}

class OneImpl implements One {

    @Override
    public void one() {
        System.out.println("One Impl");
    }
}

class NewFunctionalityDecorator implements One{

    private One oneAsComposition;

    NewFunctionalityDecorator(One oneAsComposition) {
        this.oneAsComposition = oneAsComposition;
    }

    @Override
    public void one() {
        oneAsComposition.one();
        // Our new functionality
    }
}

public static void main(String[] args) {
    One one = new NewFunctionalityDecorator(new OneImpl());
    one.one();
}

Hurray, we can achieve the same functionality without inheritance. One more surprise for my readers, this is a Decorator Design Pattern.

Let us do one more time to review how to achieve the Decorator Pattern in code.

interface Parent{
void method();
}

class Child implements Parent{
@Override
public void method() {
}
}

class Decorator implements Parent{

private Parent parent;

Decorator(Parent parent) {
this.parent = parent;
}

@Override
public void method() {
parent.method();
// Decoration code around the base functionality
}
}

We always have an Interface, and then we have a one base class that is inherited from the interface. Now, if we want to add new functionality that is independent but needs to execute base functionality before its execution, we will create a pure new class with the name Decorator. The decorator class needs to implement the same interface and need to ask the same interface as a Constructor Parameter.
We achieve the Decorator Design Pattern. Trust on me, this is so powerful, but for now, I am only focusing on how to achieve. So only try to grab the technique.

Our Decorator Class always uses inheritance + composition at the same time, and this is the key to the Decorator Design Pattern.

class Decorator implements Interface{
private Interface i;
Decorator(Interface i){
this.i = i;
}
}

The above code is the key technique, next we have some methods in the interface which we need to implement in our Decorator class due to the inheritance.

class Decorator implements Interface{
    private Interface i;
    Decorator(Interface i){
        this.i = i;
    }

    @Override
    public void someMethod() {
        // This is the method which we need to implement due to the inheritance
    }
}

Now, this is the second point, which we already discussed in the above example, where we will decide the decoration. Now first, we will call the composition or Constructor parameter method call, and then we will implement our new implementation, as shown below.

class Decorator implements Interface{
    private Interface i;
    Decorator(Interface i){
        this.i = i;
    }

    @Override
    public void someMethod() {
        i.someMethod();
        // Out decoration logic
    }
}

I hope things are clear to everyone. Now, I am going to show you the above achievement in the form of Pictures.

So we have a Base implementation and I also have a separate Decorator as shown above. I am going to achieve the above code in the form of a picture.

In case, now we want to decorate some functionality before the base or original logic execution. Are we able to achieve this? Yes, we can.

So as per our above implementation, we learn something new. We are also able to achieve pre-decoration. So don’t observe only if base execution complete and then want to execute some independent logic will use Decorator Design Pattern. Instead, we can achieve a scenario, in which we want to run some logic before the base logic execution.

By the way, in both pre and post scenarios, the client will make no changes in the main or calling site.

public static void main(String[] args) {
    // In Post Scenario
    Interface i = new Decorator(new Impl());
    // In Pre Scenario
    Interface i = new Decorator(new Impl());
}

Now, we are going to the next step. Its time to create a Pre and Post decorators so we can see all together and we will see how this will work. You will see some magic here :).

interface Interface{
void someMethod();
}

class Impl implements Interface{
@Override
public void someMethod() {
System.out.println("Original Logic");
}
}

class PreDecorator implements Interface{
private Interface anInterface;

PreDecorator(Interface anInterface) {
this.anInterface = anInterface;
}

@Override
public void someMethod() {
System.out.println("Pre Logic");
anInterface.someMethod();
}
}

class PostDecorator implements Interface{

private Interface anInterface;
PostDecorator(Interface anInterface) {
this.anInterface = anInterface;
}

@Override
public void someMethod() {
anInterface.someMethod();
System.out.println("Post Logic");
}
}

So the above code is simple. Only this time, we have two decorators. The main magic will happen when the client uses this code.

public static void main(String[] args) {
Interface anInterface = new Impl();
anInterface = new PreDecorator(anInterface);
anInterface = new PostDecorator(anInterface);
anInterface.someMethod();

// OR

Interface anInterface = new PostDecorator(new PreDecorator(new Impl()));
anInterface.someMethod();
}

As we can see in the above code, the client can call as a one-liner or as an independent wrapping of decorators.

Output:
Pre Logic
Original Logic
Post Logic

Here one cool thing, change the order of Pre and Post Decorators wrapping, but the output will remain the same.

public static void main(String[] args) {

    Interface anInterface1 = new PostDecorator(new PreDecorator(new Impl()));
    anInterface1.someMethod();
    System.out.println("--------------------------");
    Interface anInterface2 = new PreDecorator(new PostDecorator(new Impl()));
    anInterface2.someMethod();
}

Output:
Pre Logic
Original Logic
Post Logic
————————————
Pre Logic
Original Logic
Post Logic

If you are confused, please try to spend some time, and review code by asking a question of why the order doesn’t matter?

Still, there are many things that we need to discuss, and due to that, until now, I did not share with you the Cheat Sheet Definition of Decorator Design Pattern. I am going to throw one more idea in which we can use the Decorator Design Pattern. Instead, I will show this technique but later in the post. For now, you can try to think. As we are adding new functionalities by adding a Decorator, I believe we are also able to remove the added Decoration. For example, I have a GUI application, in which if a user is subscribed, we will add an extra badge on the UI. I can say we have a SubscribeGUIDecorator, and now there is a possibility user can unsubscribe the subscription, now we can create a UnSubscribeGUIDecorator, which will remove the Subscribe badge. We will discuss this use case later but try to think on these lines.

Now, in this section, I am going to show you one real-world example, maybe you can use in the production or perhaps not but for learning purpose I feel this is a Good example.

Before going to start, revise the basic technique of the Decorator Design Pattern, as we discuss above.

Our Decorator Class always uses inheritance + composition at the same time, and this is the key to the Decorator Design Pattern.

class Decorator implements Interface{
private Interface i;
Decorator(Interface i){
this.i = i;
}
}

Cache:

I am sure. Everyone knows what Cache is. So I am going to show you step by step Cache implementation, in which we will make mistakes, but in the end, we will have the fantastic and scalable implementation of our Cache.

  • Cache Implementation
  • LRUCache Implementation
  • By Inheritance
  • By Composition
  • Decorator Design Pattern ( LRUCache Decorator, TimerCache Decorator)

Note: To make things simple, I am not using any generics, but we can refactor our Cache to generic once we complete this section.

Cache Implementation:

// Java
class Cache {

    private HashMap<String, String> map;

    public Cache(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException("Capacity should be greater then 0");
        map = new HashMap<>(capacity);
    }

    public Cache() {
        this(10);
    }

    public String get(String key){
        return map.get(key);
    }
    
    public void put(String key, String value){
        map.put(key, value);
    }
    
    public String remove(String key){
        return map.remove(key);
    }
}
// Kotlin
class Cache constructor(capacity: Int = 10) {
    private val map: HashMap<String, String>
    init {
        require(capacity > 0) { "Capacity should be greater then 0" }
        map = HashMap(capacity)
    }

    operator fun get(key: String): String? {
        return map[key]
    }

    fun put(key: String, value: String) {
        map[key] = value
    }

    fun remove(key: String): String? {
        return map.remove(key)
    }
}

We did nothing complicated, only we wrapped a one HashMap inside of a Cache class and implemented our required Cache.

LRUCache Implementation:

I am sure everyone knows, what is Least Recently Use Cache. In case someone is new to this, please try to do some search on Google before reading the next code.

// Java
class LRUCache {

    private int capacity;
    private HashMap<String, String> map;
    private List array;

    public LRUCache(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException("Capacity should be greater then 0");
        this.capacity = capacity;
        map = new HashMap<>(capacity);
        array = new ArrayList(capacity);
    }

    public LRUCache() {
        this(10);
    }

    public String get(String key){
        String value = map.get(key);
        if(value!=null){
            array.remove(key);
            array.add(key);
        }
        return value;
    }

    public void put(String key, String value){
        map.put(key, value);
        array.remove(key);
        array.add(key);
        if(array.size() > capacity){
            String removedKey = (String) array.remove(0);
            map.remove(removedKey);
        }
    }

    public String remove(String key){
        return map.remove(key);
    }
}
// Kotlin
class LRUCache constructor(capacity: Int = 10) {

    private val capacity: Int
    private val map: HashMap<String, String>
    private val array: ArrayList<String>
    init {
        require(capacity > 0) { "Capacity should be greater then 0" }
        this.capacity = capacity
        map = HashMap(capacity)
        array = ArrayList(capacity)
    }

    operator fun get(key: String): String? {
        val value = map[key]
        if (value != null) {
            array.remove(key)
            array.add(key)
        }
        return value
    }

    fun put(key: String, value: String) {
        map[key] = value
        array.remove(key)
        array.add(key)
        if (array.size > capacity) {
            val removedKey = array.removeAt(0) as String
            map.remove(removedKey)
        }
    }

    fun remove(key: String): String? {
        return map.remove(key)
    }
}

Nothing complicated, only we added an LRU logic by using an array.

From a client perspective, we have two independent implementations, Cache and LRUCache. They are not able to provide us any polymorphism because we are not using any inheritance. Also, we can see we have a lot of duplication of code. I know, some of the readers know, when we have a duplication of code, it’s mean to refactor to inheritance. 🙂 

By Inheritance:

// Java
class Cache {

    private HashMap<String, String> map;

    public Cache(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException("Capacity should be greater then 0");
        map = new HashMap<>(capacity);
    }

    public Cache() {
        this(10);
    }

    public String get(String key){
        return map.get(key);
    }

    public void put(String key, String value){
        map.put(key, value);
    }

    public String remove(String key){
        return map.remove(key);
    }
}


class LRUCache extends Cache {

    private int capacity;
    private List array;

    public LRUCache(int capacity) {
        super(capacity);
        array = new ArrayList(capacity);
    }

    public LRUCache() {
        this(10);
    }

    public String get(String key){
        String value = super.get(key);
        if(value!=null){
            array.remove(key);
            array.add(key);
        }
        return value;
    }

    public void put(String key, String value){
        super.put(key, value);
        array.remove(key);
        array.add(key);
        if(array.size() > capacity){
            String removedKey = (String) array.remove(0);
            super.remove(removedKey);
        }
    }

    public String remove(String key){
        return super.remove(key);
    }
}
// In Kotlin
open class Cache constructor(capacity: Int = 10) {
    private val map: HashMap<String, String>
    init {
        require(capacity > 0) { "Capacity should be greater then 0" }
        map = HashMap(capacity)
    }

    open operator fun get(key: String): String? {
        return map[key]
    }

    open fun put(key: String, value: String) {
        map[key] = value
    }

    open fun remove(key: String): String? {
        return map.remove(key)
    }
}


class LRUCache constructor(capacity: Int = 10) : Cache(capacity) {

    private val capacity: Int = capacity
    private val array: ArrayList<String> = ArrayList(capacity)

    override fun get(key: String): String? {
        val value = super.get(key)
        if (value != null) {
            array.remove(key)
            array.add(key)
        }
        return value
    }

    override fun put(key: String, value: String) {
        super.put(key, value)
        array.remove(key)
        array.add(key)
        if (array.size > capacity) {
            val removedKey = array.removeAt(0) as String
            super.remove(removedKey)
        }
    }

    override fun remove(key: String): String? {
        return super.remove(key)
    }
}

Now, in the above code, due to inheritance, we can remove a HashMap from LRUCache, and second, the client can use the Polymorphism in his code.

public static void main(String[] args) {
Cache cache = new Cache(10);
Cache lruCache = new LRUCache(10);
}

Until now, looks okay, because we have only one level inheritance. As we know, we have a plan to create a new type of Cache which name is TimerCache. In TimerCache, Cache will remove the value on its own after the given time passed. Like I want after every one minute cache will remove all the values which are in the Cache from last one minute. Now, in this case, what should we do? Should I extend my TimerCache from LRUCache? I think this doesn’t make sense, because we don’t want the LRUCache functionality in our TimerCache. So it’s mean I should do extend my TimerCache with Cache class. Yes, now we have two classes which are inheriting from Cache class. Any change in Cache most probably will impact on both our inherited classes due to inheritance. One more question, now, due to business requirement, we also need a cache in which we want LRUCache + TimerCache functionality together. To achieve this by using inheritance is possible, but I will not recommend it.

For the learning purpose, try to think about how you will achieve LRUCache + TimerCache functionality by using inheritance or if you have time try to write code, so you can see how much your code will be in a bad shape

By Composition:

// In Java
class Cache {

    private HashMap<String, String> map;

    public Cache(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException("Capacity should be greater then 0");
        map = new HashMap<>(capacity);
    }

    public Cache() {
        this(10);
    }

    public String get(String key){
        return map.get(key);
    }

    public void put(String key, String value){
        map.put(key, value);
    }

    public String remove(String key){
        return map.remove(key);
    }
}


class LRUCache{

    private int capacity;
    private Cache cache;
    private List array;

    public LRUCache(int capacity) {
        cache = new Cache(capacity);
        array = new ArrayList(capacity);
    }

    public LRUCache() {
        this(10);
    }

    public String get(String key){
        String value = cache.get(key);
        if(value!=null){
            array.remove(key);
            array.add(key);
        }
        return value;
    }

    public void put(String key, String value){
        cache.put(key, value);
        array.remove(key);
        array.add(key);
        if(array.size() > capacity){
            String removedKey = (String) array.remove(0);
            cache.remove(removedKey);
        }
    }

    public String remove(String key){
        return cache.remove(key);
    }
}
// In Kotlin
class Cache constructor(capacity: Int = 10) {
    private val map: HashMap<String, String>
    init {
        require(capacity > 0) { "Capacity should be greater then 0" }
        map = HashMap(capacity)
    }

    open operator fun get(key: String): String? {
        return map[key]
    }

    open fun put(key: String, value: String) {
        map[key] = value
    }

    open fun remove(key: String): String? {
        return map.remove(key)
    }
}

class LRUCache constructor(capacity: Int = 10) {

    private val capacity: Int = capacity
    private val cache: Cache = Cache(capacity)
    private val array: ArrayList<String> = ArrayList(capacity)

    fun get(key: String): String? {
        val value = cache.get(key)
        if (value != null) {
            array.remove(key)
            array.add(key)
        }
        return value
    }

    fun put(key: String, value: String) {
        cache.put(key, value)
        array.remove(key)
        array.add(key)
        if (array.size > capacity) {
            val removedKey = array.removeAt(0) as String
            cache.remove(removedKey)
        }
    }

    fun remove(key: String): String? {
        return cache.remove(key)
    }
}

As you can see in the above code, we are able to achieve LRUCache by using composition.

public static void main(String[] args) {
Cache cache = new LRUCache(10); // Will not compile
LRUCache lruCache = new LRUCache(10); // Will work
TimerCache timer = new TimerCache(10); // Will work
}

As you can see in the above example, now we are not able to use Cache as a Polymorphism because now there is no more Inheritance.
Until now, we see the difficulties in scalability with inheritance, and when we refactor to Composition, then we lost the relationship between these Caches due to no inheritance from Cache. So its time to fix everything and make our code more scalable, maintainable by implementing Decorator Design Pattern. As I already discuss with you in Decorator Design Pattern we will use inheritance+composition together.

Decorator Design Pattern ( LRUCache Decorator, TimerCache Decorator):

First Step, need a common interface.

// In Java
interface ICache{
    int capacity();
    String get(String key);
    void put(String key, String value);
    String remove(String key);
}
// In Kotlin
interface ICache {
    val capacity: Int
    fun get(key: String): String?
    fun put(key: String, value: String)
    fun remove(key: String): String?
}

As per requirement, we create a new interface as shown above.

Second Step, implement ICache on the base class, which is a Cache class in our case, as shown below.

// In Java
class Cache implements ICache{

    private HashMap<String, String> map;
    private int capacity;

    public Cache(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException("Capacity should be greater then 0");
        this.capacity = capacity;
        map = new HashMap<>(capacity);

    }

    public Cache() {
        this(10);
    }

    @Override
    public int capacity() {
        return capacity;
    }

    public String get(String key){
        return map.get(key);
    }

    public void put(String key, String value){
        map.put(key, value);
    }

    public String remove(String key){
        return map.remove(key);
    }
}
// In Kotlin
class Cache constructor(override val capacity: Int = 10) : ICache{

    private val map: HashMap<String, String>
    init {
        require(capacity > 0) { "Capacity should be greater then 0" }
        map = HashMap(capacity)
    }

    override fun get(key: String): String? {
        return map[key]
    }

    override fun put(key: String, value: String) {
        map[key] = value
    }

    override fun remove(key: String): String? {
        return map.remove(key)
    }
}

Until now, we did no significant changes, instead, only we added one new method capacity(), and the remaining code is the same.

Step three, as we already discussed, Decorator class is always inherited from the Interface + compose with the same Interface as a Constructor Parameter ( Composition ). In our case, LRU is a Decoration around the Cache :).

// In Java
class LRUCache implements ICache{

    private ICache cache;
    private List array;

    public LRUCache(ICache iCache) {
        cache = iCache;
        array = new ArrayList(iCache.capacity());
    }

    @Override
    public int capacity() {
        return cache.capacity();
    }

    public String get(String key){
        String value = cache.get(key);
        if(value!=null){
            array.remove(key);
            array.add(key);
        }
        return value;
    }

    public void put(String key, String value){
        cache.put(key, value);
        array.remove(key);
        array.add(key);
        if(array.size() > capacity()){
            String removedKey = (String) array.remove(0);
            cache.remove(removedKey);
        }
    }

    public String remove(String key){
        return cache.remove(key);
    }
}
// In Kotlin
class LRUCache constructor(private val cache: ICache) : ICache{

    private val array: ArrayList<String> = ArrayList(capacity)
    override val capacity: Int
        get() = cache.capacity

    override fun get(key: String): String? {
        val value = cache.get(key)
        if (value != null) {
            array.remove(key)
            array.add(key)
        }
        return value
    }

    override fun put(key: String, value: String) {
        cache.put(key, value)
        array.remove(key)
        array.add(key)
        if (array.size > capacity) {
            val removedKey = array.removeAt(0) as String
            cache.remove(removedKey)
        }
    }

    override fun remove(key: String): String? {
        return cache.remove(key)
    }
}

Again, you can see in the above code. We made no significant changes. Only I inherited with ICache, and I added a Constructor Argument ICache and the remaining code is the same. Now, we will see how the Client will use this.

public static void main(String[] args) {
ICache cache = new Cache(10);
ICache LRUCache = new LRUCache(new Cache(10));

// Change cache to LRUCache at runtime
ICache runtimeChangeInExistingFunctionality = new LRUCache(cache); // WOW
}

Now, if the client wants to use the only Cache, he can use Cache only. In case he wants LRUCache in some features, he is allowed to decorate with LRUCache. Amazing. Now, we will add the TimerCache so we can make ourselves more confident, and we can see more power of this Decorator Design Pattern.

Note: from now on I am only writing code in Kotlin 🙂

class TimerCache constructor(private val cache: ICache) : ICache {

private var isPolling: Boolean = false;
private val map = HashMap<String, Long>(cache.capacity)

init {
isPolling = true
thread(start = true) {
while (isPolling) {
poll()
Thread.sleep(1000 * 60)
}
}
}

private fun poll() {
println("Polling... ${Thread.currentThread().name}")
val currentSeconds = System.currentTimeMillis() / 1000
map.forEach { (key, seconds) ->
if (currentSeconds > seconds + 1) {
cache.remove(key)
map.remove(key)
println("Remove $key")
}
}
}

override val capacity: Int
get() = cache.capacity

override fun get(key: String): String? {
return cache.get(key)
}

override fun put(key: String, value: String) {
cache.put(key, value)
map[key] = System.currentTimeMillis() / 1000
}

override fun remove(key: String): String? {
return cache.remove(key)
}

}

This code is simple. I created an additional map inside of our TimerCacheDecorator, which is responsible for time elapsed. After every one minute, our poll method is called. In the poll, we are checking if some value is elapsed more then 1 minute, then remove that value from the cache. Nothing complex. But there is one more point which you guys need to focus. As you can see in TimerCache, our get and remove methods are not doing any pre or post decorations; instead, they delegate directly to ICache methods and only put the method in which we added some logic. Its mean in Decoration Design Pattern we don’t need to play with all methods. Instead, there is a possibility in some cases that only we add functionality in one method, and the remaining code should work in the same way.

Now, we will see how our client will use our API’s.

fun main() {
    val cache : ICache = Cache(10)
    val lruCache: ICache = LRUCache(Cache(10))
    val timerCache: ICache = TimerCache(Cache(10))
}

Now, the business has a new requirement. We want to use LRUCache + TimerCache together. What should we do?

fun main() {
    // New Business Requirement LRUCache + TimerCache
    val lruPlusTimer =  LRUCache(TimerCache(Cache(10)))
    
    // above line we can write by using let
    val lruPlusTimerWithLet =  Cache(10)
        .let(::TimerCache)
        .let(::LRUCache)
}

Wow, it is impressive. Now business finds out one issue with the current requirement. So business wants to change LRUCache + TimerCahce to TimerCache + LRUCache. 🙂

fun main() {
// New Business Requirement LRUCache + TimerCache
val lruPlusTimer = TimerCache(LRUCache(Cache(10)))

// above line we can write by using let
val lruPlusTimerWithLet = Cache(10)
.let(::LRUCache)
.let(::TimerCache)
}

Decorator Design Pattern in UI:

I am going to create a straightforward UI in Android, but I hope that will help us to grab the concept of UI implementation by using the Decorator Design Pattern. There are two ways to implement UI, one I will create all UI in one XML file, and then our decorators will take care of there visibility or second we can add different UI components at runtime. I am going with a one XML strategy because that will be very simple, and we can focus on our primary goal, Decorator Design Pattern.

Note: I am using a straightforward example, but you can use this same strategy in the case of Complex UI. Mostly we have UI’s in RecyclerViews, which depend on some flags.

We want to achieve above UI. Business requirement is,

  • If a user is a free user, only do enable the Free button.
  • If a user is a Subscribed user, then do enable Free and Subscription Button.
  • If a user is a paid user, then do enable Free and Paid Button.

First Step, we need an interface.

interface ProfileUI{
    fun render(view: View)
}

Second Step, we need a base or core functionality class.

class Base : ProfileUI{
override fun render(view: View) {
view.Free.isEnabled = true
}
}

Before going to start working on the Decorator Design Pattern. I am going to create a Factory Class who is responsible for Creation.

fun main() {
    val base = Base()// Inflation of UI
    Factory.decorate("", base)
}

object Factory {

 fun decorate(s: String, profileUI: ProfileUI): ProfileUI {
    // Logic of Decoration   
 }
}

Now, in the main method, we are showing our Profile, but from now, we will do our remaining implementation in the Factory class.

class SubscriptionProfileDecorator(private val profileUI: ProfileUI) : ProfileUI {
override fun render(view: View) {
profileUI.render(view)
view.Subscription.isEnabled = true
}
}

Above code is the implementation of SubscriptionProfile Decoration.

class PaidProfileDecorator(private val profileUI: ProfileUI) : ProfileUI {
override fun render(view: View) {
profileUI.render(view)
view.Paid.isEnabled = true
}
}

The above code is the implementation of PaidProfile Decoration.

Now, its time to see the implementation of the main logic in Factory.

object Factory {

    fun decorate(s: String, profileUI: ProfileUI): ProfileUI {
        if (s == "Subscription")
            return SubscriptionProfileDecorator(profileUI)
        else if (s== " Paid")
            return PaidProfileDecorator(profileUI)

        throw IllegalArgumentException("Illegal profile type")
    }
}

Wow, we achieve the Decoration Design Pattern in the perspective of UI. Now, the business wants to add a new feature in which when we will get “All” as Profile type, we should do enable all Buttons. Its time to achieve this functionality.

class AllProfileDecorator(private val profileUI: ProfileUI): ProfileUI{
    override fun render(view: View) {
        PaidProfileDecorator(SubscriptionProfileDecorator(profileUI))
    }
}

Simple, now add a new type in Factory.

fun decorate(s: String, profileUI: ProfileUI): ProfileUI {
    if (s == "Subscription")
        return SubscriptionProfileDecorator(profileUI)
    else if (s == "Paid")
        return PaidProfileDecorator(profileUI)
    else if (s == "All") // New changes
        return AllProfileDecorator(profileUI) 

    throw IllegalArgumentException("Illegal profile type")
}

Cool, we achieve this new change in minutes, and as a developer, I am 100% confident there will be no new bugs in my existing implementation.

Now, the business wants when we will get the profile type “Paid Negative” at a refresh of UI. We need to disable Paid Button.

class PaidNegativeDecorator(private val profileUI: ProfileUI) : ProfileUI {
    override fun render(view: View) {
        profileUI.render(view)
        view.Paid.isEnabled = false
    }
}

Done, now we will add this new change in the Factory class.

object Factory {

    fun decorate(s: String, profileUI: ProfileUI): ProfileUI {
        if (s == "Subscription")
            return SubscriptionProfileDecorator(profileUI)
        else if (s == "Paid")
            return PaidProfileDecorator(profileUI)
        else if (s == "All")
            return AllProfileDecorator(profileUI)
        else if (s == "PaidNegative") // New change
            return PaidNegativeDecorator(profileUI)

        throw IllegalArgumentException("Illegal profile type")
    }
}

I hope this example gives you enough information and now you can implement Decorator Design Pattern, please don’t implement this Pattern everywhere. Instead, spend some time on your requirements and then decide is your use case of Decorator Design Pattern or not.

Its time to check Decorator Design Pattern usage in different SDK classes. By the way, I will show the classes only and will not explain the implementation. Try your best to observe the code and make your self confident how these SDK classes are Decorator Design Pattern.

Java SDK 1.8:

new BufferedReader(new InputStreamReader(System.in));
new LineNumberReader(
        new BufferedReader(
                new InputStreamReader(System.in)
        )
);
new BufferedReader(
new LineNumberReader(
new InputStreamReader(System.in)
)
);

The above code blocks are the implementation of the Decorator Design Pattern.

Cheat Sheet Definition:

Decorator Design Pattern is an inheritance + composition in the same class.

interface I{}
class Base : I{}
class DecoratorOne(val i: I) : I{}
class DecoratorTwo(val i: I) : I{}
main{
new DecoratorOne(new DecoratorTwo(new Base()))
}

Conclusion:

In conclusion, we will repeat what we learn today.

Q: Where should we use Decorator Design Pattern?
Ans:

  • If you have one level inheritance and due to some reason you are going to create a new class from the same interface, try to observe maybe you have a Decorator Design Patter Use case.
  • We want to add new functionality, which will be executed once the existing logic is complete. In this case, we can refactor to the Decorator Design Pattern.
  • We want to add new functionality, which will be executed before the existing logic call, then we can explore the Decoration Design Patter.
  • In some cases, we want to add an independent logic around some code, but also we want in some particular cases to reverse that logic. We can choose the Decorator Design Pattern.
  • Anytime in your code, you are going to call super.something() in the child class, there is a possibility of a Decorator Design Patter.

Q: What are the steps we need to use to implement the Decoration Design Pattern?
Ans: You should have one interface, one base class extending from the interface. Then you can create Decorators how many you want only by doing inheritance + composition using the same interface in the same class.

interface BaseInterface{}
class Base implements BaseInterface{}
class DecoratorOne implements BaseInterface{
private BaseInterface baseInterface;
DecoratorOne(BaseInterface baseInterface) {
this.baseInterface = baseInterface;
}
}
class DecoratorTwo implements BaseInterface{
private BaseInterface baseInterface;
DecoratorTwo(BaseInterface baseInterface) {
this.baseInterface = baseInterface;
}
}

More Posts on Design Patterns https://www.uwanttolearn.com/design-patterns/

Facebooktwitterredditpinterestlinkedinmailby feather

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.