📃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
AbstractClass
y 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
ConcreteClass
aAbstractClass
indica queConcreteClass
hereda 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());
}
}