Patrón Método Plantilla

Recuerdo una vez en la universidad, cuando cursaba Programación I, que el profesor me preguntó lo siguiente:

¿Hacer un avión de papel es un algoritmo?

En ese momento dudé bastante, y no estaba seguro de mi respuesta. Ahora con más experiencia puedo decirles que respondí bien en aquel momento.

La respuesta es sí. Hacer un avión con una hoja de papel es un algoritmo. ¿Por qué? Pues es sencillo, un algoritmo podría decirse que son una serie de pasos definidos para obtener un resultado. En este caso, los pasos serían doblar la hoja de papel de varias maneras hasta obtener el resultado final que sería un avión de papel.

El patrón Método Plantilla lo podemos utilizar cuando queremos encapsular la lógica de nuestros algoritmos. En otras palabras, cuando queremos definir una serie de pasos (métodos) que juntos, nos dan un resultado específico.

Por ejemplo, tenemos nuestra aplicación que se encarga de calcular el salario neto (luego de aplicar todos los gastos) para los empleados de la empresa y para los consultores.

Los empleados de la empresa tienen un salario fijo y sus deducciones son del 11% del salario. Por otro lado, los consultores tienen un salario por hora y tienen una deducción del 5%.

Además, los empleados de la empresa reciben el dinero directamente en sus cuentas bancarias, mientras que los consultores reciben un cheque.

Veamos el diagrama de clases.

Diagrama de clases de ejemplo patrón método plantilla

Si prestamos atención a los métodos de ambas clases, podemos identificar similitudes entre ambas. Las dos clases se encargan de procesar el pago según su tipo y en ambos casos es necesario notificar al contador del pago.

Veamos el diagrama de clases con esta sugerencia.

Diagrama de clases mejorado

Este nuevo diagrama luce mejor, sin embargo, aún podemos mejorarlo. ¿Recuerdan el avión de papel y el porque es un algoritmo? En este caso también tenemos una serie de pasos para procesar el pago:

  1. Obtener el salario, ya sea fijo o por hora.
  2. Calcular el porcentaje de deducción.
  3. Enviar el dinero, ya sea por transferencia o cheque.
  4. Notificar al contador del pago.

Es decir, tanto los empleados como los consultores poseen un algoritmo para procesar el pago. Dentro de este algoritmo existen diferencias para cada clase, pero el objetivo y el orden de los pasos es el mismo.

Intentemos abstraer un poco más nuestro diseño. Por ejemplo, ajustemos los nombres de los métodos que se encargan de obtener el salario, la deducción y el envío del dinero.

Diagrama de clases usando herencia

Con este diseño logramos abstraer aún más nuestras clases, ya que, las clases hijas de PayablePerson deberán implementar sus propias versiones de getSalary, getDeduction y sendPayment.

El método processPayment es nuestro Método Plantilla, ya que en él vamos a encapsular los pasos para procesar los pagos. Es decir, este método sólo va a ser implementado en PayablePerson para que de este modo, los hijos se vean forzados a seguir los pasos.

Para lograr esto, es necesario definir el método processPayment con la palabra final.

/*
* This is our template method
*/
public final void processPayment() {
    getSalary();
    getDeduction();
    sendPayment();
    notifyAccountant();
}

El libro Head First Design Patterns define este patrón de la siguiente forma: El patrón Método Plantilla define el esqueleto de un algoritmo dentro de un método y delega algunos pasos a las subclases. Le permite a las subclases redefinir ciertos pasos del algoritmo sin cambiar su estructura.

El objetivo principal de este patrón es definir una plantilla o estructura para algún proceso/algoritmo dentro de nuestra aplicación. En el ejemplo anterior, primero identificamos los diferentes pasos del proceso de pago, luego definimos el orden de esos pasos y finalmente los encapsulamos en un sólo método que no puede ser modificado por las subclases.

Este sería el diagrama de clases para el patrón Método Plantilla.

Diagrama de clases sugerido para el patrón método plantilla

Veamos las diferencias que existen, en el ejemplo anterior, al utilizar el método plantilla.

Sin el patrón:

  1. Employee y Consultant se deben de encargar de realizar el proceso y controlar el algoritmo.
  2. El código se duplica en ambas clases.
  3. Si la estructura del algoritmo cambia, es necesario modificar ambas clases.
  4. Incluir una persona más que necesita procesar el pago es difícil.
  5. Los detalles del algoritmo están dispersos entre todas las clases.

Con el patrón:

  1. PayablePerson se encarga del proceso y de controlar el algoritmo.
  2. Se reutiliza código en la clase abstracta.
  3. La estructura del algoritmo está en un sólo lugar., por lo cual es más fácil de cambiar.
  4. El método plantilla ofrece un framework al resto de subclases, por lo cual solamente se deben implementar ciertos métodos.
  5. Los detalles del algoritmo se encuentran en un sólo lugar.

Una de las ventajas que nos ofrece este patrón es la de poder crear “anzuelos” o “ganchos” (hooks). Estos anzuelos se utilizan para brindar información adicional y poder acceder a partes específicas dentro del algoritmo.

Por ejemplo, podríamos crear un método adicional dentro de PayablePerson, para que luego de notificar al departamento de contabilidad, envíe o no una copia del comprobante de pago al empleado. Este nuevo método, por defecto, puede indicar que no es necesario enviar una copia y de esta forma cada subclase puede decidir si envía o no un comprobante.

Ejemplo de clase tipo anzuelo

El uso de estos anzuelos sigue el siguiente principio de diseño definido en el libro Head First Design Patterns: No nos llame, nosotros los llamamos.

Pero, ¿qué significa cuando hablamos de programación orientada a objetos? Este principio de diseño nos indica que los componentes de alto nivel (abstracciones) pueden brindar anzuelos a componentes de bajo nivel (subclases) pero solamente los de alto nivel deciden cómo y cuando utilizar esos anzuelos.

Al utilizar este principio nos aseguramos que al implementar los anzuelos (definidos en las clases abstractas) desde las subclases, van a ser utilizados por las abstracciones en el momento justo y solamente si son necesarios.

Este sería el resultado de ejecutar nuestro código de ejemplo.

Resultado de ejecutar el código de ejemplo

El código de ejemplo lo pueden descargar aquí.

En el próximo post vamos a analizar el patrón llamado Iterador.

Recordá suscribirte aquí para recibir las últimas actualizaciones todas las semanas.

Referencias:
Libro Head First Design Patterns. Versión digital.

Leave a Reply

Your email address will not be published. Required fields are marked *