Table of content

Decorator Pattern in Kotlin

decorator-pattern-kotlin

The Decorator pattern allows a user to add new functionality to an existing object without altering its structure. This type of design pattern comes under structural pattern as this pattern acts as a wrapper to the existing class.

That is there will be additional features added to the original object. The component, which adds these additional features is called the Decorator.

The Decorator class in this pattern act as a wrapper object which dynamically attaches additional features to the original object at run-time.

Definition:

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality

As in the definition, the main intent of the decorator pattern is to attach additional responsibilities and features dynamically to an object. However, at the same time, other instances or base class will not be affected by the new dynamic change. Only the individual object will receive the new modification.

Since this pattern solves the dynamic feature addition at run-time, it resolves the problem of the complex sub-classing requirement when extending base class functionality.

Example for Decorator Design Pattern

It’s like adding cheese topping to a pizza or adding walnut and chocolate source topping to an ice-cream. You can add any kind of cheese or sauce to the pizza just before serving to the customer, yet base pizza remains unchanged.

Likewise, you can request walnut or cherries on a vanilla ice cream or you can change the mind and ask to add only chocolate topping and cherry sauce. Base vanilla ice cream won’t change and you can have whatever topping you like on the ice cream.

If there are 20 people in the group, they can request 20 vanilla ice creams with different toppings, nuts, and fruits on time of serving.

Problem solved by the Decorator Pattern

The problem occurs when an application wants to incorporate additional features and behaviours to objects at run-time. Moreover, it's not only one feature, but there can also be multiple features and functionalities.

Therefore, acquiring that situation will definitely become a lengthy and complex coding hack to a developer. You may think, inheritance and sub-classing will help to resolve the situation. However, those won’t support due to certain limitations on those approaches.

Inheritance and sub-classing effects at compile-time that is statically, you have to pre-configure the required additional features and functionalities if planning to use those approaches.

In addition, the new options will attach to all the objects from those sub-classes, which is not a definite requirement at this situation. Hence, we need a way to fill both of those gaps. Decorator comes into the play to solve this problem.

What is the Decorator Class?

Decorator class is the component, which inserts new options to the base object dynamically. Additionally, it will not do any modification to other objects from the same class since it inserting the new options at run-time.

The ‘Decorator’ is an interface or an abstract class in kotlin programming. It provides a platform to implement additional features and functionalities. Then several classes will take the Decorator as the base and implement the concrete additional behaviors, which are requested by the original object at run-time.

Hence, ‘Decorator’ acts as a wrapper object to the original object.

Example for Decorator Design Pattern

Let’s take a juice bar which makes different kinds of juices. Their most popular product is the milkshake. There are many flavors of milkshakes to select.

Imagine three friends went there and ordered a banana milkshake, peanut butter shake, and chocolate milkshake.

We need to represent this scenario for the decorator pattern implementation. If we study the scenario further, we can identify that the base component is milk with sugar and salt. Then there are three kinds of additional features attached to this milkshake on the serving time or blending moment dynamically.

Thus, we can break our class structure as below,

    • MilkShake interface : This is the base component interface for both the original object and decorator object
interface MilkShake {
    fun getTaste()
}
    • ConcreteMilkShake : This is the actual base milkshake which use to make the customer required flavor of milkshake
class ConcreteMilkShake : MilkShake {
    override fun getTaste() {
        println("It’s milk !")
    }
}
    • MilkShakeDecorator : This is the abstract decorator for the subsequent concrete decorator classes.
open class MilkShakeDecorator(protected var milkShake: MilkShake) : MilkShake {
    override fun getTaste() {
        this.milkShake.getTaste()
    }
}
    • BananaMilkShake : This is one of the concrete implementations of the decorator interface. This decorator class adds the banana flavor decoration to the concrete milkshake. Banana is the additional state or feature in this particular instance.
class BananaMilkShake(m:MilkShake) : MilkShakeDecorator(m){

    override public fun getTaste(){
        super.getTaste ();
        this.addTaste();
        println(" It’s Banana milk shake !");
    }
    public fun addTaste(){
        println(" Adding Banana flavor to the milk shake !");
    }
}
  • PeanutButterShake : Another Special decoration for this milkshake instance is peanut butter, Implemented just like BananaMilkShake
  • main() : The function which requests the specific instance from the juice bar


The complete code for Decorator design pattern in kotlin

interface MilkShake {
    fun getTaste()
}

class ConcreteMilkShake : MilkShake {
    override fun getTaste() {
        println("It’s milk !")
    }
}
open class MilkShakeDecorator(protected var milkShake: MilkShake) : MilkShake {
    override fun getTaste() {
        this.milkShake.getTaste()
    }
}

class BananaMilkShake(m:MilkShake) : MilkShakeDecorator(m){

    override public fun getTaste(){
        super.getTaste ();
        this.addTaste();
        println(" It’s Banana milk shake !");
    }
    public fun addTaste(){
        println(" Adding Banana flavor to the milk shake !");
    }
}

public class PeanutButterMilkShake(m:MilkShake) : MilkShakeDecorator(m){

    override public fun getTaste(){
        super.getTaste ();
        this.addTaste();
        println(" It’s Peanut butter milk shake !");
    }
    public fun addTaste(){
        println(" Adding Peanut butter flavor to the milk shake !");
    }
}

fun main(args: Array<String>) {
    val peanutMilkShake = PeanutButterMilkShake(ConcreteMilkShake())
    peanutMilkShake.getTaste()
    val bananaMilkShake = BananaMilkShake(ConcreteMilkShake())
    bananaMilkShake.getTaste()
}

The output of the kotlin decorator pattern

It’s milk !
 Adding Peanut butter flavor to the milk shake !
 It’s Peanut butter milk shake !
It’s milk !
 Adding Banana flavor to the milk shake !
 It’s Banana milk shake !
Comment / Suggestion Section
Point our Mistakes and Post Your Suggestions