Los principios de diseño del software

Los principios de diseño del software

Siguiendo nuestro curso de programación hoy veremos los principios de software en los que, en gran medida, se basan los patrones de diseño. Es importante saber que estos principios son una herramienta para hacer una refactorización correcta de un código con malos síntomas, no una herramienta para usar los patrones, pues en la gran mayoría de los casos usando estas buenas prácticas no sería necesario utilizar ningún patrón. Tampoco debemos meter nunca un patrón a calzador.

principios_diseño

Sobre todo podemos destacar seis principios fundamentales, aunque son bastantes más:

  • Principio de responsabilidad única o Single responsibility principle (SRP)
  • Principio de abierto - cerrado o Open-closed principle (OCP)
  • Principio de sustitución de Liskov o Liskow substitution principle (LSP)
  • Principio de inversión de dependencias o Dependecy inversion principle (DIP)
  • Principio de segregación de interfaces o Interface segregation principle (ISP)
  • Principio de Hollywood.

Principio de responsabilidad única (SRP)

El principio de responsabilidad única dictamina que una clase debería tener un único motivo de cambio. Definimos la responsabilidad como una razón para el cambio. Veamos un ejemplo:

Supongamos que tenemos una clase Employee con un montón de responsabilidades:

ante_srp

¿Estaría bien? No, hace demasiadas cosas, no debería hacer tantas cosas. Aplicando el principio de responsabilidad única debería quedar algo así:

aplicado_srp

Principio de abierto - cerrado (OCP)

El principio de abierto - cerrado enuncia que las entidades software (clases, módulos, funciones...) deberían estar abiertas para la extensión, pero cerradas para la modificación. Cuando simple cambio a un programa resulta en una cascada de cambios en módulos dependientes, el diseño adolece de rigidez. Los cambios se deben hacer añadiendo código nuevo, no modificando el que ya funcionaba.

Supongamos que tenemos que Cliente y Servidor son dos clases concretas. El cliente usa el servidor, ¿qué pasa si queremos cambiar el servidor? Tendríamos que cambiar la implementación del cliente.

antes_ocp

Podemos solucionar este problema teniendo una interfaz que se comunique entre ambas implementaciones.

aplicado_ocp

Principio de sustitución de Liskov (LSP)

El principio de sustitución de Liskov dice que los subtipos deben poder ser reemplazados por cualquiera de sus tipos base. Los objetos de un programa deberían ser reemplazables por instancias de sus subtipos sin alterar el correcto funcionamiento de un programa. Debemos asegurarnos de que las clases derivadas extiendan la clase base sin alterar su comportamiento de manera que se viole el contrato.

La violación de este principio, en Java, suele dar como resultado un código repleto de expresiones del tipo instanceof en las cláusulas de un if anidado. Tenemos que tener claro que los usuarios de los tipos base no deberían hacer nada especial para tratar a los tipos derivados. De hecho, no deberían ser conscientes siquiera de la existencia de esos subtipos.

Supongamos que tenemos una jerarquía de empleados en las que existen empleados con un sueldo fijo y con un sueldo condicionado por las horas de trabajo.

antes_lsp

Supongamos ahora que tenemos un nuevo empleado que no tiene sueldo, ¿qué hacemos? recalculamos el método de calcular sueldo por 0? no tendría sentido alguno, nos obligaria a poner en alguna parte del código un instanceof o algo similar. La solución es muy sencilla, no incluir el nuevo empleado en la jerarquía, pues no son un subtipo de empleado que cumpla todas sus restricciones, en este caso tener un sueldo.

Principio de inversión de dependencias (DIP)

El principio de inversión de dependencias enuncia que:

  1. Los módulos de alto nivel no deberían depender de módulos de bajo nivel. Ambos deben depender de abstracciones.
  2. Las abstracciones no deben depender de detalles. Son los detalles los que deben depender de abstracciones.

Es decir, hay que depender de abstracciones no de implementaciones concretas. Programar para una interfaz no para una implementación. Veamos un ejemplo:

antes_dip

Estamos haciéndolo justo al revés. Lo podemos solucionar utilizando una interfaz.

aplicado_dip

Principio de segregación de interfaces (ISP)

Es mejor muchas interfaces específicas para cada cliente que una sola interfaz de propósito general. Es decir, los clientes no deberían depende de métodos que no usan. Este lo veremos con más calma pues en cierta medida llega a ser contradictorio. Por ahora podemos fijarnos en:

aplicado_srp

Como es vital la segregación de las interfaces en casi cualquier refactorización de código.

Principio de Hollywood.

El principio de Hollywood dictamina que debe ser una clase superior la que llame a las clases inferiores, por ejemplo en la herencia debe ser la clase padre quien llame a sus subclases. Es denominado principio de Hollywood porque se basa en el "Don´t call us, we´ll call you", (no nos llame, nosotros le llamamos) tan empleado en Hollywood cuando un actor va a un casting.

Los ejemplos más claros donde se puede ver cómo se aplica este principio son en los conocidos patrones de diseño Observer, Factory Method, Strategy, y sobre todo, el más evidente, el patrón Template Method. Este principio no es del todo correcto pues entra en conflicto con otros principios, más adelante veremos cual es la finalidad concreta de este principio.

En los siguientes artículos miraremos como podemos aplicar estos principios a nuestro código y como también surge la necesidad de utilizar los patrones de diseño. Vamos a terminar dejando el catálogo de patrones de diseño que revisaremos en los siguientes artículos. Podemos distinguirlos en tres categorías:

  • Patrones de creación: Abstract Factory, Factory Method, Singleton, Prototype…
  • Patrones estructurales: Adapter, Composite, Decorator…
  • Patrones de comportamiento: Command, State, Stretagy, Observer, Template Method, Visitor…
Para ti
Queremos saber tu opinión. ¡Comenta!