Cuando empiezas a trabajar con patrones de diseño en Kotlin, uno de los primeros que suele aparecer es el patrón Template Method. No es casualidad: es sencillo de entender, muy potente en determinados contextos y, si se usa bien, puede ahorrar bastante duplicación de código.
El patrón de diseño Template Method es una herramienta muy importante al momento de crear nuestra aplicaciones en Kotlin y en esta entrada hablaremos sobre el mismo y como emplearlo en nuestras aplicaciones, el siguiente artículo intenta dar luces sobre el siguiente tema cómo se implementa en Kotlin y cuándo es recomendado utilizarlo.
Eso sí, también es un patrón que puede volverse problemático si se aplica sin criterio. En este artículo te explico qué es el patrón Template Method en Kotlin, cómo implementarlo correctamente y, sobre todo, cuándo tiene sentido usarlo y cuándo conviene evitarlo, basándome tanto en teoría como en experiencia práctica.
¿Qué es el Patrón Template Method?
El patrón Template Method es un patrón de diseño de comportamiento que define la estructura general de un algoritmo en una superclase (a menudo abstracta) y deja los detalles específicos para ser implementados por las clases hijas.
Permite a las subclases personalizar partes específicas del algoritmo sin alterar su estructura general, es decir, podemos crear variantes de la clase fácilmente para reutilizar algún comportamiento y poder implementar código propio de la subclase.
Con esto, se consigue evitar la duplicidad de código, poder reutilizar fácilmente la clase adaptando comportamientos personalizados.
Como desventaja radica en que si necesitas cambiar la clase base o superclase es probable que debas de cambiar TODAS las clases relacionadas o subclases
El método de plantilla o Template Method es un patrón de diseño muy simple que separa las partes de clase a compartir o superclase mediante una clase principal abstracta que contiene los pasos del algoritmo y permita que las clases heredadas sobrescriben los pasos individuales
Definición y objetivo del patrón
El Template Method es un patrón de diseño de comportamiento que define la estructura general de un algoritmo en una clase base, delegando en las subclases la implementación de ciertos pasos concretos.
La idea es simple:
- El flujo del algoritmo no cambia.
- Algunas partes del comportamiento sí pueden variar.
En Kotlin, esto encaja de forma natural gracias a:
- Clases abstractas.
- Métodos abstractos u open.
- El hecho de que los métodos sean final por defecto.
Propósito del Patrón
El patrón Template Method se utiliza para:
- Evitar la duplicación de código.
- Permitir la creación de variantes de una clase base.
- Reutilizar la lógica de la clase base en múltiples subclases.
El problema que resuelve en programación orientada a objetos
Este patrón aparece cuando tienes varias clases que:
- Siguen exactamente el mismo flujo.
- Pero difieren en pequeños detalles.
En lugar de duplicar ese flujo en cada clase, el Template Method permite centralizar la lógica común y evitar errores, inconsistencias y código repetido.
Esto se hizo evidente al implementar un juego, donde distintos personajes compartían un mismo ciclo de acciones, pero cada uno ejecutaba su comportamiento específico.
Cómo funciona el patrón Template Method
- El concepto de método plantilla
- El corazón del patrón es el método plantilla:
un método que define el orden exacto de los pasos del algoritmo.
- El corazón del patrón es el método plantilla:
- En Kotlin, este método:
- No suele marcarse como open.
- Controla el flujo completo.
- Llama a métodos que las subclases deben implementar.
Clases abstractas y pasos variables en Kotlin
La estructura típica es:
- Una clase abstracta.
- Un método público que define el flujo.
- Métodos abstractos o open para los pasos variables.
Esto obliga a que todas las subclases respeten el mismo orden de ejecución, algo especialmente útil cuando el flujo no debería modificarse.
Implementación del Patrón Template Method
El patrón Template Method se implementa de la siguiente manera:
- Crear una clase abstracta (llamada
Template) que tenga un método público llamadotemplateMethody un método abstracto llamadodoPartOfSomethingque se utiliza dentro deltemplateMethod. De esta manera, se obliga a todas las clases que heredan deTemplatea implementar ese método. - Las clases concretas (por ejemplo,
Template1yTemplate2) pueden tener diferentes estructuras. A veces, el métododoPartOfSomethingpuede tener una implementación predeterminada, y las subclases pueden proporcionar variantes específicas en lugar de implementarlas desde cero. - Es común agregar más de un método abstracto a la clase
Template. Si deseas que la claseTemplatese comporte como una clase base, utiliza la delegación de interfaces en lugar de una clase abstracta para evitar la mayoría de las desventajas de este patrón.
Ejemplo
Supongamos que estamos creando un sistema de personajes para un juego de rol por turnos. Tenemos dos tipos de personajes: guerreros y magos. Cada personaje ataca de manera diferente. Aquí está la implementación en Kotlin:
abstract class Character {
fun completeTurn() {
println("Finishing a turn with ${attack()} attack")
}
protected abstract fun attack(): String
}
class Mage : Character() {
override fun attack() = "Fireball"
}
class Warrior : Character() {
override fun attack() = "Sword"
}
fun main() {
val warrior = Warrior()
warrior.completeTurn()
val mage = Mage()
mage.completeTurn()
// Compilation error: 'attack' is protected
warrior.attack()
}
En este ejemplo, Character es la clase abstracta que define el método templateMethod (completeTurn) y el método abstracto doPartOfSomething (attack). Las clases concretas Mage y Warrior implementan el método attack según sus propias características.
Como puedes apreciar del código anterior, la clase principal o superclase expone algunos métodos abstractos que la subclase o clase derivada implementará, como puedes ver, parte de la lógica común que en este caso es el println("Finishing a turn with ${attack()} attack") se reutilizará en el resto de las subclases.
Aqui tienes otro ejemplo:
abstract class AbstractClass { // clase padre
// No puedes anular este método porque no es `open`
fun templateMethod() { // el Método de Plantilla, es decir, el algoritmo con pasos ordenados
println("ejecutando el método de plantilla")
primitiveOperation1() // llamando a los pasos del algoritmo
primitiveOperation2()
primitiveOperation3()
}
abstract fun primitiveOperation1() // paso del algoritmo a ser implementado por la clase concreta
private fun primitiveOperation2() { // paso del algoritmo que no debe ser anulado
println("realizando operación abstracta 2")
}
abstract fun primitiveOperation3() // otro paso
}
class ConcreteClass : AbstractClass() { // clase concreta
override fun primitiveOperation1() { // implementación del paso para la clase concreta
println("realizando operación concreta 1")
}
override fun primitiveOperation3() {
println("realizando operación concreta 3")
}
}
class AnotherConcreteClass : AbstractClass() { // otra clase concreta
override fun primitiveOperation1() {
println("realizando operación concreta 1") // implementación duplicada, no es ideal
}
override fun primitiveOperation3() {
println("realizando otra operación concreta 3")
}
}En esta implementación, empleamos el método templateMethod() en la clase abstracta de la superclase, en este método se especifican como deben ser invocados cada método, y con esto, podemos tener una implementación predeterminada que podrá ser traspasado a las clases heredadas para utilizar esta plantilla.
Este método no puede ser sobrescrito mediante las clases heredadas, ya que no es de tipo open o abstracto Kotlin bloquea su sobrescritura desde las clases hijas y permite la reutilización del mismo en las clases hijas.
Ejemplo 2
Estructura básica con clases abstractas
Un ejemplo sencillo en Kotlin sería este:
abstract class Character {
fun completeTurn() {
prepare()
action()
finish()
}
protected open fun prepare() {
println("Preparando turno")
}
protected abstract fun action()
protected open fun finish() {
println("Finalizando turno")
}
}Aquí:
- completeTurn() es el método plantilla.
- action() es obligatorio para las subclases.
- prepare() y finish() pueden reutilizarse o sobrescribirse.
Las subclases solo se preocupan de su comportamiento específico:
class Warrior : Character() {
override fun action() {
println("Atacando con espada")
}
}
class Mage : Character() {
override fun action() {
println("Lanzando hechizo")
}
}Este enfoque reduce muchísimo la duplicación y hace que el código sea más fácil de razonar.
Caso real: uso del Template Method en un juego en Kotlin
Comportamientos comunes y flujo compartido
El patrón Template Method encajó especialmente bien al desarrollar un juego, donde necesitaba que distintos personajes siguieran el mismo flujo de acciones.
Cada turno tenía pasos claros:
- Preparar el estado.
- Ejecutar la acción principal.
- Resolver efectos y finalizar.
El orden no cambiaba, pero la acción sí. Centralizar ese flujo en una clase base me permitió mantener el sistema coherente sin duplicar lógica.
Por qué encaja bien en sistemas de turnos o acciones
En sistemas de turnos:
- El flujo es predecible.
- Las variaciones están bien delimitadas.
- Añadir nuevos personajes es frecuente.
Gracias al Template Method, añadir un nuevo tipo de personaje consistía simplemente en implementar su acción específica, sin tocar el resto del sistema. Eso hizo que el código fuera más estable y fácil de extender.
Ventajas del patrón Template Method en Kotlin
- Reutilización de código
- La ventaja más clara es evitar repetir lógica común. Todo el flujo vive en un único lugar.
- Control del flujo del algoritmo
- El patrón garantiza que nadie pueda alterar el orden de ejecución accidentalmente, algo crítico en sistemas sensibles como juegos o pipelines de procesamiento.
- Facilidad para añadir nuevas variantes
- Cuando el diseño está bien pensado, crear nuevas subclases es rápido y seguro, algo que personalmente agradecí cuando el número de personajes empezó a crecer.
Desventajas y riesgos del Template Method
- Acoplamiento por herencia
- El mayor problema del patrón es que depende de la herencia. Las subclases quedan fuertemente ligadas a la clase base.
- Dificultad para extender el flujo base
- Si necesitas modificar el flujo general, probablemente tendrás que revisar todas las subclases. Esto puede convertirse en un dolor de mantenimiento.
Cuándo se convierte en un problema
Si:
- El flujo cambia con frecuencia.
- Las subclases empiezan a necesitar excepciones constantes.
- El árbol de herencia crece demasiado.
Entonces el Template Method deja de ser una buena opción.
Template Method vs Strategy en Kotlin
Diferencias clave
- Template Method usa herencia y controla el flujo.
- Strategy usa composición y permite intercambiar comportamientos dinámicamente.
Cuándo elegir cada uno
- Usa Template Method cuando el flujo es fijo.
- Usa Strategy cuando el comportamiento debe cambiar en tiempo de ejecución.
En Kotlin, Strategy suele ser más flexible gracias a lambdas y funciones de orden superior.
Alternativas modernas al Template Method en Kotlin
- Higher-order functions
- Kotlin permite definir funciones que reciben otras funciones como parámetros, logrando un efecto similar sin herencia.
- Extension functions como método plantilla funcional
- Un ejemplo muy común es el uso de funciones de extensión para encapsular un flujo y delegar solo la parte variable al consumidor, algo que personalmente considero más idiomático en Kotlin moderno para casos simples.
Cuándo usar (y cuándo evitar) el patrón Template Method en Kotlin
- Úsalo cuando:
- El flujo es estable.
- Hay muchas variantes con diferencias pequeñas.
- Quieres imponer una estructura clara.
- Evítalo cuando:
- Necesitas mucha flexibilidad.
- El sistema cambia constantemente.
- Kotlin ofrece una alternativa más simple con funciones.
Preguntas frecuentes sobre el patrón Template Method en Kotlin
- ¿Template Method sigue siendo recomendable en Kotlin moderno?
- Sí, pero con criterio. No siempre es la opción más idiomática.
- ¿Se puede implementar sin herencia?
- No exactamente, aunque las funciones de orden superior pueden cubrir casos similares.
- ¿Es buena idea usarlo en videojuegos?
- Sí, especialmente en sistemas de turnos o flujos bien definidos.
Conclusión
El patrón Template Method en Kotlin sigue siendo una herramienta válida, especialmente en sistemas con flujos bien definidos como juegos o procesos por etapas. En mi experiencia, bien aplicado puede simplificar mucho el diseño y evitar errores.
Eso sí, no es la solución universal. Kotlin ofrece alternativas modernas que, en muchos casos, encajan mejor. La clave está en entender el problema antes de elegir el patrón.
El patrón Template Method es útil cuando necesitas crear una estructura general para un algoritmo y permitir que las subclases personalicen partes específicas. Sin embargo, ten en cuenta que este patrón puede ser difícil de mantener y puede requerir cambios en todas las subclases si necesitas extender la superclase. Utilízalo con precaución y considerar otras alternativas si es posible.