Behavioral Design Pattern

Kotlin Design Patterns: Template Method Explained

Michal Ankiersztajn
ProAndroidDev
Published in
3 min readApr 29, 2024

--

Purpose of the pattern

Overriding only a specific algorithm step without changing its general structure. You have a general algorithm structure in the superclass, but make one of the abstract steps so that the subclasses must implement it.

It’s used to either create variants of the class or to reuse base class logic in multiple subclasses.

What do we get from that?

  • Avoidance of code duplication.
  • Replaceable parts of the program.
  • You’re able to add new subclasses without modification of existing subclasses.

However, this pattern can be a trap! It’s tough to maintain. If you need to extend the superclass, you’ll likely have to change all subclasses. Unfortunately, it must use inheritance, and the superclass might limit some of the subclasses.

Implementation

Template method diagram

Here Template is an abstract class that has a public templateMethod and abstract doPartOfSomething that is used inside templateMethod . This way, we force all classes that inherit from Template to implement that method. Here Template1 and Template2 can have different structures.

Sometimes the doPartOfSomething method can have a default implementation. Then, the subclasses will make different variants instead of implementing them from scratch.

It’s also common to add more than a single abstract method to the Template . If you want the Template to behave like a base class, use Interface delegation instead of abstract class to prevent most of the disadvantages of this pattern.

Example

Your Task is to create a Character System. You’ll have to create Warrior and Mage , likely, you’ll also have to add more characters. It’s a turn-based game and each Character will attack differently. It’s a good use case for a TemplateMethod because all of them are based on something in general.

Character system diagram

Let’s start by creating our abstract class Character as others depend on it:

abstract class Character {
fun completeTurn() {
println("Finishing a turn with ${attack()} attack")
}

protected abstract fun attack(): String
}

Now, for the concrete classes:

class Mage : Character() {
override fun attack() = "Fireball"
}

class Warrior: Character() {
override fun attack() = "Sword"
}

Now for the usage:

fun main() {
val warrior = Warrior() // Finishing a turn with Sword attack
warrior.completeTurn()
val mage = Mage() // Finishing a turn with Fireball attack
mage.completeTurn()

warrior.attack() // Compilation error attack is protected
}

If Mage or Warrior need to be extended by additional functions, they can do so without modifying the existing codebase, which is important to keep in mind when coding.

You might also want to make attack public instead of hiding it under completeTurn .

In general, I think I would discourage you from using this pattern if possible.

Thanks for reading! Please clap if you learned something, and follow me for more!

Learn more about design patterns:

Design Patterns In Kotlin

17 stories

Based on the book:

“Wzorce projektowe : elementy oprogramowania obiektowego wielokrotnego użytku” — Erich Gamma Autor; Janusz Jabłonowski (Translator); Grady Booch (Introduction author); Richard Helm (Author); Ralph Johnson (Author); John M Vlissides (Author)

--

--