Head First Design Patterns: 2 of 10

I just learnt my second design from the Head First Design Pattern book. Today, I learnt about the Decorator pattern.

According to Head First Design Patterns, the Decorator pattern is a pattern used to attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

This simply means Decorators allow you to add new behaviours to a class by wrapping them in a wrapper class. A wrapper is an object that has a reference to a target class and delegates all actions to the target class.
Decorators must be of the same type of the wrapped class. This is what makes the decorator pattern different from a normal wrapper.

Decorators come in handy situations such as these:

  1. Adding new behaviour to classes you don't own.
  2. A situation whereby using inheritance would lead to too many classes because of the different scenarios that must be covered by the software. For example, you were asked to design a software that calculates the total price of food for a Nigerian native restaurant. Because of the various combinations of solids and soups, there would be a lot of classes to create and maintain.

In situations such as these, you can use the Decorator pattern. Here's how to implement the Decorator pattern.

abstract class Solid {
    abstract val description: String
    abstract val price: Int
}

class Eba : Solid() {
    override val description: String
        get() = "Eba"
    override val price: Int
        get() = 100
}

class Fufu : Solid(){
    override val description: String
        get() = "Fufu"
    override val price: Int
        get() = 200

}
abstract class SolidDecorator(val solid: Solid) : Solid() {
    abstract override val description: String
}

class NativeSoupDecorator(solid: Solid) : SolidDecorator(solid) {
    override val description: String
        get() = "${solid.description} with Native Soup"
    override val price: Int
        get() = solid.price + 500
}

class AfangSoupDecorator(solid: Solid) : SolidDecorator(solid) {
    override val description: String
        get() = "${solid.description} with Afang Soup"
    override val price: Int
        get() = solid.price + 500
}

Like I said before, Decorators are a special kind of wrappers. Take a look at the SolidDecorator abstract class. It has a reference to a Solid class and it also subclasses Solid. This is what makes Decorators special. You can then create many concrete classes of the SolidDecorator adding new behaviours to the initial wrapped class. Decorators can also add their behaviour before and/or after delegating actions to the target object.

var order : Solid = Eba()
    order = NativeSoupDecorator(solid = order)
    order = AfangSoupDecorator(solid = order)
    order = FishDecorator(order)
    println("${order.description} price: ${order.price}")

    var order2 : Solid = Eba()
    order2 = NativeSoupDecorator(solid = order2)
    order2 = GoatMeatDecorator(solid = order2)
    println("${order2.description} price: ${order2.price}")

Eba, Native Soup, Afang Soup, goat meat price: 2100
Eba, Native Soup, goat meat price: 650

An example of the decorator pattern in android development can be found in this library: https://github.com/nhaarman/ListViewAnimations

26