📃TemplateMethod
Ejemplo WTF
Esto es lo que no tenemos que hacer
Claro, aquí tienes un resumen conciso de los puntos clave del patrón de diseño Template Method:
Concepto Principal
- El Template Method es un patrón de diseño que define el esqueleto de un algoritmo en un método, postergando algunos pasos algorítmicos a las subclases. Esto permite que las subclases puedan alterar ciertos pasos del algoritmo sin cambiar su estructura general.
Uso
- Se utiliza cuando varios algoritmos tienen estructuras similares pero difieren en algunos pasos específicos.
- Ayuda a evitar la duplicación de código y promueve la reutilización al centralizar el código común en una clase base.
Estructura
- Clase Abstracta: Define un método plantilla (
templateMethod()) que contiene un algoritmo con pasos definidos, algunos de los cuales son implementados por subclases. - Subclases Concretas: Implementan los detalles específicos de los pasos del algoritmo que varían entre las diferentes implementaciones.
Ventajas
- Reutilización de Código: Centraliza el código común, reduciendo la duplicación.
- Extensibilidad: Facilita la introducción de nuevas variantes de un algoritmo sin modificar el código existente.
- Control: El flujo del programa es controlado desde el nivel superior, permitiendo una mejor gestión del comportamiento del software.
Desventajas
- Rigidez: Puede resultar en jerarquías de clases rígidas si las subclases tienen que seguir una estructura de método plantilla muy estricta.
- Complejidad: Puede ser difícil seguir el flujo de control cuando el algoritmo es complejo y distribuido a través de múltiples subclases.
Aplicaciones Típicas
- Muy usado en frameworks y bibliotecas donde se define un algoritmo estándar con puntos de extensión específicos para que los usuarios los adapten según necesiten.
Este patrón es fundamental para diseñar aplicaciones robustas y fácilmente extensibles, manteniendo un alto nivel de coherencia y cohesión en el código base.
Estructura
La imagen que has proporcionado ilustra el patrón de diseño Template Method a través de un diagrama UML (Unified Modeling Language). Este diagrama muestra la relación entre una clase abstracta y una clase concreta en el contexto de este patrón. A continuación, te explico cada uno de los elementos del diagrama:
-
AbstractClass:
- Es una clase abstracta que define un método llamado
TemplateMethod(). Este método es el método plantilla que dicta el algoritmo general. En el contexto del patrón, este método invoca una serie de operaciones en un orden específico. - Contiene declaraciones de métodos llamados
PrimitiveOperation1()yPrimitiveOperation2(). Estos son llamados dentro deTemplateMethod()y son los pasos del algoritmo que varían según la implementación específica. Por lo tanto, se definen como operaciones, pero su implementación exacta se deja a las subclases.
- Es una clase abstracta que define un método llamado
-
ConcreteClass:
- Esta es una subclase de
AbstractClassy proporciona implementaciones concretas de los métodosPrimitiveOperation1()yPrimitiveOperation2(). - Cuando
TemplateMethod()se ejecuta en una instancia deConcreteClass, se llamarán las implementaciones dePrimitiveOperation1()yPrimitiveOperation2()deConcreteClass.
- Esta es una subclase de
-
Flecha de Herencia:
- La flecha sólida de
ConcreteClassaAbstractClassindica queConcreteClasshereda deAbstractClass.
- La flecha sólida de
-
Líneas discontinuas con marcadores de llamada:
- Las líneas discontinuas que apuntan a los métodos
PrimitiveOperation1()yPrimitiveOperation2()desdeTemplateMethod()indican que estos métodos son invocados (llamados) porTemplateMethod(). En UML, estas líneas discontinuas suelen representar llamadas a métodos o utilización de métodos definidos en otro lugar.
- Las líneas discontinuas que apuntan a los métodos
Este diagrama UML está comunicando que AbstractClass establece un algoritmo en TemplateMethod(), pero deja algunos de los detalles específicos de ese algoritmo para ser definidos por ConcreteClass. De esta manera, las clases concretas que extienden AbstractClass pueden alterar partes del algoritmo sin cambiar la estructura general del mismo.
Ejemplo Práctico (Ejercicio 4 Cálculo de sueldos)
Sea una empresa que paga sueldos a sus empleados, los cuales están organizados en tres tipos: Temporarios, Pasantes y Planta. El sueldo se compone de 3 elementos: sueldo básico, adicionales y descuentos.
| Temporario | Pasante | Planta | |
|---|---|---|---|
| básico | $ 20.000 + cantidad de horas que trabajo * $ 300 | $20.000 | $ 50.000 |
| adicional | $5.000 si está casado $2.000 por cada hijo | $2.000 por examen que rindió | $5.000 si está casado $2.000 por cada hijo $2.000 por cada año de antigüedad |
| descuento | 13% del sueldo básico 5% del sueldo adicional | 13% del sueldo básico 5% del sueldo adicional | 13% del sueldo básico 5% del sueldo adicional |
Tareas
- Diseñe la jerarquía de Empleados de forma tal que cualquier empleado puede responder al mensaje #sueldo.
- Desarrolle los test cases necesarios para probar todos los casos posibles.
- Implemente en Java.
Diagrama
Codigo
Empleado
public abstract class Empleado {
private String nombre;
protected double cantidadHijos;
protected Boolean estaCasado;
public Empleado(String nombre) {
this.nombre = nombre;
}
public double calcularSueldo() {
return this.getBasico() + getAdicional() - getDescuento();
}
abstract double getBasico();
abstract double getAdicional();
double getDescuento() {
return this.getBasico() * 0.13 + this.getAdicional() * 0.05;
}
public void setCasado(Boolean estaCasado) {
this.estaCasado = estaCasado;
}
public void cantidadHijos(double cantidadHijos) {
this.cantidadHijos = cantidadHijos;
}
}
Empresa
public class Empresa {
List<Empleado> empleados = new ArrayList<Empleado>();
public void agregarEmpleado(Empleado e) {
this.empleados.add(e);
}
}
Pasante
public class Pasante extends Empleado{
private double examenesRendidos;
public Pasante(String nombre) {
super(nombre);
}
@Override
double getBasico() {
return 20000;
}
@Override
double getAdicional() {
return 2000 * this.examenesRendidos;
}
public void setExamenesRendidos(double cantidadExamenes) {
this.examenesRendidos = cantidadExamenes;
}
}
Planta
public class Planta extends Empleado{
private double antiguedad;
public Planta(String nombre) {
super(nombre);
// TODO Auto-generated constructor stub
}
public void setAntiguedad(double antiguedad) {
this.antiguedad = antiguedad;
}
@Override
double getBasico() {
return 50000;
}
@Override
double getAdicional() {
double total = 0;
if (this.estaCasado) {
total = total + 5000;
}
return total + (2000 * this.cantidadHijos) + (2000 * this.antiguedad);
}
}
Temporal
public class Temporal extends Empleado{
private double cantidadHoras;
public Temporal(String nombre) {
super(nombre);
}
public void setCantidadHoras(double horas) {
this.cantidadHoras = horas;
}
@Override
double getBasico() {
return 20000 + (this.cantidadHoras * 300);
}
@Override
double getAdicional() {
if (this.estaCasado) {
return 5000 + (2000 * this.cantidadHijos);
}
return 2000 * this.cantidadHijos;
}
}
Test
public class EmpleadoTest {
Pasante pasante;
Planta planta;
Temporal temporal;
@BeforeEach
void setUp() throws Exception {
temporal = new Temporal("Temporal");
pasante = new Pasante("Pasante");
planta = new Planta("Planta");
}
@Test
public void testTemporal() {
temporal.setCantidadHoras(10);
temporal.setCasado(true);
temporal.cantidadHijos(1);
assertEquals(23000, temporal.getBasico());
assertEquals(7000, temporal.getAdicional());
assertEquals((23000 * 0.13) + (7000 * 0.05), temporal.getDescuento());
}
@Test
public void testPasante() {
pasante.setExamenesRendidos(10);
assertEquals(20000, pasante.getBasico());
assertEquals(20000, pasante.getAdicional());
assertEquals((20000 * 0.13) + (20000 * 0.05), pasante.getDescuento());
}
@Test
public void testPlanta() {
planta.setCasado(true);
planta.cantidadHijos(1);
planta.setAntiguedad(1);
assertEquals(50000, planta.getBasico());
assertEquals(9000, planta.getAdicional());
assertEquals((50000 * 0.13) + (9000 * 0.05), planta.getDescuento());
}
}