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 */ }
}