Implementando SOLID – LSP

Este es el tercer post de la serie Implementando SOLID, la cual surge de compartir con otros colegas las resoluciones que cada uno implementaría  al aplicar los principios SOLID. En esta ocasión vamos a implementar el principio de sustitución de Liskov pero antes de comenzar les comparto los links a otras resoluciones:

Resolución por Fernando: http://blog.kudewe.com/2012/08/implementando-solid-lsp.html

Liskov substitution principle:

El principio de sustitución de Liskov junto al principio abierta cerrado (OCP) son los que nos definen el camino para aplicar correctamente la herencia en el diseño orientado a objetos. Esto se debe a que, juntos, dan robustez al diseño y con robustez me refiero a la “capacidad para adaptarse a los cambios” en forma estable. Mientras que OCP hace que nuestro diseño pueda evolucionar de forma “segura” (principalmente utilizando herencia), LSP nos marca el camino de esa evolución para no abusar de dicha herencia.

Siguiente con los ejercicios utilizados en el evento, refactorizaremos el llamado LSP.MailBuilder de la siguiente forma:

  1. Ejecutamos los tests, todo verde, empezamos a refactorizar.
  2. Identificamos al método WithEntity de la clase ContactInformationMailBuilder como el objetivo de nuestra refactorización, según LSP no debería ser necesario conocer las clases derivadas de ContactInformation para funcionar, sino que debería funcionar con la clase base (en este caso ContactInformation) siendo sus derivadas las encargadas de extender el comportamiento.
  3. Pasamos el método ParseContactInformation a la clase ContactInformation.
  4. Ejecutamos los tests, todos verde.
  5. Sobrescribimos el método ParseContactInformation en la clase ContactInformationSubsidiary, agregando el comportamiento del método ParseContactInformationSubsidiary.
  6. Ejecutamos los tests, todos verde.
  7. Sobrescribimos el método ParseContactInformation en la clase ContactInformationAuction, agregando el comportamiento del método ParseContactInformationAuction.
  8. Ejecutamos los tests, todos verde.
Unable to display content. Adobe Flash is required.

En este momento, la familia de la clase ContactInformation aun tiene una pequeña responsabilidad en el parseo y es la de determinar la forma de relacionar el nombre de la propiedad con el valor, para solucionar esto hacemos un refactor mas: “Cambiar los parámetros de AddBodyLine a propertyName y propertyValue”.

Unable to display content. Adobe Flash is required.

Por último y para respetar el principio de responsabilidad única (SRP), podríamos hacer que el parser sea un objeto aparte y no una Action (callback), separando así la responsabilidad de parsear un ContactInformation de la de construir un Mail.

Unable to display content. Adobe Flash is required.

 

Otros artículos de la serie:

  • Principio de responsabilidad única
  • Principio abierto / cerrado

Implementando SOLID–OCP

Este es el segundo post de la serie Implementando SOLID, en esta ocasión vamos a implementar el principio Abierto (a la extensión) Cerrado (a la modificación).

Open close principle:

Los (micro)pasos seguidos para aplicar este principio se puede separar en 3 grupos de refactorización, el primer refactor tiene mucho que ver en el principio de responsabilidad única que vimos en el post anterior ya que estamos separando el criterio de filtrado de la aplicación de este criterio sobre la lista de productos. En el segundo refactor nos terminamos de preparar para aplicar el principio OCP (pasos 1 y 2) y lo aplicamos (paso 4). En el tercer refactor podemos ver como extendemos el funcionamiento sin modificar el código anterior.

Refactor 1: Separando responsabilidades de ProductoFilter.ByColor:
  1. Run tests, todo en verde, refactorizamos.
  2. En ProductFilter, extraemos el método Match a partir de la condición de filtrado.
  3. Run tests, todo en verde.
  4. Creamos la clase ColorCriteria, movemos el método Match a esta nueva clase y ajustamos el código.
  5. Run tests, todo en verde.
  6. Pasamos el productColor como parámetro en el constructor y ajustamos el código.
  7. Cambiamos el parámetro productColor del método ByColor por el criteria, cambiando de lugar la instanciación de ColorCriteria a la invocación de este método. Ajustamos el código.
  8. Run tests, todo en verde.


ver screencast

Refactor 2: generalizando ProductFilter.ByColor y ProductFilter.BySize
  1. Extraemos una interface de ColorCriteria, que llamaremos ICriteria y contendrá el método Match.
  2. Reemplazamos el type del parámetro criteria de ProductoFilter.ByColor a ICriteria y cambiamos el nombre del método a ByCriteria.
  3. Run tests, todo en verde.
  4. Creamos una nueva implementación de ICriteria para Size que llamaremos SizeCriteria, tomamos el código de ProductFilter.BySize, el cual borraremos y en su lugar invocaremos a ByCriteria. Ajustamos el código.
  5. Run tests, todo en verde.


ver screencast

Refactor 3: agregando implementación de ICriteria para filtrar por color y tamaño
  1. Creamos una nueva implementación de ICriteria llamada MultiCriteria que haga el Match contra todos los elementos de una lista de ICriteria.
  2. Borramos el método ByColorAndSize de ProductFilter y reemplazamos sus invocaciones por ByCriteria con MultiCriteria. Ajustamos el código.
  3. Run tests, todo en verde.


ver screencast

código fuente final: clic aquí

Y aquí les comparto la solución de Fernando para este mismo caso y la de Martín