Buenas prácticas de Desarrollo Limpio S.O.L.I.D.

Carlos Bermúdez 
Director de Arquitectura

Jeffrey Tobón Cataño
Analista de Desarrollo

El Área de Arquitectura de Cidenet busca promover las buenas prácticas de desarrollo y avanzar en la excelencia técnica, por dicha razón, compartimos los principios S.O.L.I.D., un conjunto de directrices para facilitar, a los desarrolladores, la labor de crear programas legibles, mantenibles y reutilizables. 

Hoy les compartimos la segunda parte con las letras L.I.D.


Liskov Substitution Principle

El Principio de Sustitución de Liskov (LSP) alude a su creadora, Barbara Liskov, y dice que “cada clase que hereda de otra puede usarse como su padre sin necesidad de conocer las diferencias entre ellas.”, es decir, los objetos padre deben poder ser reemplazados por instancias de sus subtipos sin alterar el correcto funcionamiento del sistema. Algunos autores lo consideran una extensión del Principio Open/Closed. 

Si S (clase hija) es un subtipo de T (clase padre), entonces los objetos de tipo T (clase padre) pueden ser sustituidos por objetos de tipo S.

Algunas recomendaciones para no violar este principio son:

  • Las clases hijas no deberían establecer pre-condiciones más fuertes ni pos-condiciones más débiles que las que establece la clase padre.
  • Valores invariantes de la clase padre no deben poder ser modificados en la clase hija.
  • Respetar las restricciones históricas, es decir, no puede existir un método en la clase hija que vaya en contra de un comportamiento de su clase padre.

Beneficios:

  • Aporta una jerarquía de clases fácil de entender.
  • Ayuda a tener código reutilizable.
  • Flexibilidad a la hora de cambiar entre clases de la superclase.

Veamos un ejemplo de violación del principio. Tenemos una clase Persona y una clase Niño, como un Niño es una Persona, podríamos hacer uso de la herencia, sin embargo, el Niño no puede tener tarjeta, ni DNI y no puede pagar, es decir, estos campos deberán ser nulos e incluso se deberá lanzar una excepción en el método pagar. Entonces, a pesar de que el Niño es un tipo de Persona, no está cumpliendo el principio de Liskov ya que en algunos puntos del programa no podríamos sustituir la clase base (Persona) por la clase hija (Niño) sin que esto implique un cambio de comportamiento significativo. Veámoslo:

Aplicando el principio de Liskov podemos rediseñar nuestra jerarquía de clases y tener un mejor diseño, es decir, queremos pasar de A a B.

Ahora el Niño si puede sustituir una Persona ya que siempre tiene nombre y apellido. Incluso el Niño podría pagar, pero delegando la responsabilidad a un Adulto a través de la figura de tutor.


Interface Segregation Principle

Hablando de este principio Bob sugiere: “Haz interfaces que sean específicas para un tipo de cliente”, es decir, para una finalidad concreta. 

Según el Principio de Segregación de Interfaces (ISP), es preferible contar con muchas interfaces que definen pocos métodos que tener una interface forzada a implementar muchos métodos a los que no se dará uso.

Beneficios:

  • Ayuda al desacoplamiento del sistema.
  • Aporta a una mejor estructuración del proyecto a nivel de código.
  • Evita la aparición de interfaces gordas (Interface bloat o fat interfaces).
  • Facilita la refactorización, modificación y despliegue
  • Evitar tener muchos métodos sin implementar (Unimplemented method)

Imaginemos unas clases necesarias para albergar algunos tipos de aves. Por ejemplo, tendríamos pericos y águilas.

Ahora queremos añadir a los pingüinos, estos son aves que no vuelan y además también nadan, si modificamos la interfaz IAve para agregar el método nadar, obligaría a todos a implementar este nuevo método, de igual forma, como el pingüino no puede volar, tendría que dejar el método volar sin implementar. Un mejor diseño podría ser el siguiente


Dependency Inversion Principle

Las 2 premisas que definen el Principio de Inversión de Dependencia (DIP) son: 

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

A través de este principio se pretende que el código núcleo y/o estable (módulos de alto nivel) de nuestra aplicación no dependan de los detalles de implementación (frameworks, base de datos, conexiones, etc), su interacción se debe realizar a través de interfaces, de tal forma que no haya que conocer las implementaciones concretas.

Beneficios:

  • Reduce el acoplamiento e incrementa la cohesión (Como otros Principios antes mencionados).
  • Código más fácil de probar y mantener
  • Cuando se domina ayuda al desarrollo Ágil.

En el siguiente ejemplo podemos ver como una clase de alto nivel, como lo es la cesta de compras depende de clases de bajo nivel, como lo son el mecanismo para almacenar y el método de pago. 

Si se quisiera agregar otros métodos de pago o cambiar el mecanismo de almacenamiento, tendríamos que desmontar casi que toda la lógica de nuestra cesta de compras. Como solución podemos aplicar el principio de inversión de dependencias, reemplazando las clases concretas por interfaces y evitando que la responsabilidad de instanciar dichas dependencias sea de la cesta de compras, esto lo logramos recibiéndolas en su constructor.

Con lo anterior damos por finalizado el análisis de cada uno de los principios asociados al acrónimo S.O.L.I.D.

Los esperamos en más entradas con temas que nos ayuden a tener un “Desarrollo Limpio”.