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.
¿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
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.
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 llamadotemplateMethod
y un método abstracto llamadodoPartOfSomething
que se utiliza dentro deltemplateMethod
. De esta manera, se obliga a todas las clases que heredan deTemplate
a implementar ese método. - Las clases concretas (por ejemplo,
Template1
yTemplate2
) pueden tener diferentes estructuras. A veces, el métododoPartOfSomething
puede 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 claseTemplate
se 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 Práctico
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.
Conclusiones
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.
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter