Motivation: The overall structure of an algorithm is fixed, but individual steps may vary across different use cases (e.g., parsing files in different formats follows the same open-read-parse-close sequence, but the parsing step differs).

Intent: Define the skeleton of an algorithm in a superclass, deferring some steps to subclasses.

The interface class controls the algorithmic flow and provides optional hooks while subclasses fill in the variable steps.

abstract class DataProcessor {
    // template method
    fun process(path: String) {
        val raw = readFile(path)
        val data = parse(raw)
        analyze(data)
    }
 
    private fun readFile(path: String): String { /* ... */ }
    abstract fun parse(raw: String): List<Any>
    open fun analyze(data: List<Any>) { /* optional hook */ }
}
 
class CsvProcessor : DataProcessor() {
    override fun parse(raw: String): List<Any> { /* CSV parsing */ }
}
 
class JsonProcessor : DataProcessor() {
    override fun parse(raw: String): List<Any> { /* JSON parsing */ }
}