One of my favorite patterns is a decorator. I love it because we store implementation in different classes and then combine them according to business specification. Our classes become small, testable and easy to understand. Moreover, we group our logic in one layer.

I think ‘decorator’ is truly one pattern that can achieve SRP

Let’s get down to the practice. Now I`m trying kotlin and all code will be written in this awesome language ;)

Here are our simple requirements: when the user clicks the button we should show the dialog with the phrase. This phrase can consist of multiple random words, joined by space.

We would write just layer responsible for ‘Phrase’

Implementation

Let`s write an implementation of our business requirements.

Standard phrase:

1
2
3
class Phrase(private val phrase: String) : PhraseInterface {
    override fun phrase() = this.phrase
}

Random phrase:

1
2
3
4
5
6
7
8
9
class RandomPhrase(
        vararg private val phrases: PhraseInterface
) : PhraseInterface {
    private val rand = Random()
    override fun phrase(): String {
        val index = rand.nextInt(phrases.size)
        return phrases[index].phrase()
    }
}

Joined by space:

1
2
3
4
5
6
7
8
class CompositePhrase(
    vararg private val items: PhraseInterface
) : PhraseInterface {
    override fun phrase(): String {
      return items.map { it.phrase() }
                  .joinToString(separator = " ")
   }
}

Configuration layer

After implementation we can continue with our configuration layout.

1
2
3
4
5
6
7
8
fun main(args: Array<String>) {
    val phrase = CompositePhrase(
      RandomPhrase(Phrase("Just"), Phrase("So")),
      RandomPhrase(Phrase("awesome"), Phrase("great")),
      RandomPhrase(Phrase("code"), Phrase("job"))
    )
    println("Result: ${phrase.phrase()}")
}

Result

If you run this code in the console (or REPL) you will see probably the similar output.

1
2
3
Result: So awesome job
Result: Just great code
Result: So awesome code

When you get new requirements, you need just to change configuration layer. If the project is new, we introduce more and more decorators. But after a while, we start to reuse them without writing the new one.

Decorator pattern is very powerful. The main difference with the standard iffor approach is code reuse. It is easy to reuse class then method. Also, it is much easier to test them. One drawback is unusual code style. But it becomes more familiar after a while. If you don’t use decorators just try it. You will definitely love it.