Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java

En este tutorial discutiremos los dos primeros principios SOLID del diseño orientado a objetos ejemplificando cada principio utilizando código Java.

Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java

Lo primero que debemos entender son las razones de porque se crearon estos principios y porque deberíamos considerarlos al momento de diseñar software. Hagamos historia:

Los principios SOLID fueron presentados por Robert C. Martin en su artículo del año 2000 “Principios de diseño y patrones de diseño“.  Estos conceptos fueron desarrollados más tarde por Michael Feathers, quien nos presentó el acrónimo SOLID. Y en los últimos 20 años, estos 5 principios han revolucionado el mundo de la programación orientada a objetos, cambiando la forma en que escribimos software.

¿qué es SOLID y cómo nos ayuda a escribir mejor código?

En pocas palabras, los principios de diseño de Martin y Feathers nos alientan a crear software más fácil de mantener, comprensible y flexible. En consecuencia, a medida que nuestras aplicaciones crecen en tamaño, podemos reducir su complejidad y ahorrarnos muchos dolores de cabeza en el futuro. Los siguientes 5 conceptos conforman los principios SOLID:

  1. Single Responsibility
  2. Open/Closed
  3. Liskov Substitution
  4. Interface Segregation
  5. Dependency Inversion
PrincipioDescripción
Single ResponsibilityUna clase debería tener solo una responsabilidad y solo una razón para cambiar
Open/ClosedLos componentes deberán estar abiertos para poder extender su funcionalidad pero cerrados para modificarlos
Liskov SubstitutionLas clases derivadas deberían ser completamente sustituibles por sus tipos base. Si la clase A es un subtipo de la clase B, deberíamos poder reemplazar con A sin interrumpir el comportamiento de nuestro programa
Interface SegregationLos clientes no deberían ser forzados a implementar métodos innecesarios que no usarán. Simplemente significa que las interfaces más grandes deben dividirse en otras más pequeñas.
Dependency InversionEl principio de inversión de dependencia se refiere al desacoplamiento de módulos de software. De esta forma, en lugar de que los módulos de alto nivel dependan de los módulos de bajo nivel, ambos dependerán de abstracciones.
Principios SOLID

Principio de Responsabilidad única (Single Responsibility)

Una clase solo debe tener una sola responsabilidad, además, debe tener solo una razón para cambiar. Veámoslo con un ejemplo.

Supongamos que estamos diseñando un software para conectarnos a la tabla de una base de datos, leer algunos datos de esa tabla y escribir los resultados de dicha lectura en un archivo JSON. Las 3 funcionalidades que tenemos son:

  1. Conectarnos a la base de datos
  2. Leer los datos de la tabla
  3. Escribir un archivo json con los datos

La clase que escribamos tendría varias razones para cambiar:

  • Por ejemplo se puede cambiar la base de datos (inicialmente leíamos los datos desde Oracle y ahora necesitamos leerlos desde MySQL)
  • Cambia el archivo json de salida porque cambiaron los datos
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Si este fuera el diseño de nuestra clase estaríamos rompiendo el principio de responsabilidad única

A la clase Client le estamos dando responsabilidades que no le competen. Una forma de corregir esto sería por ejemplo llevar la lógica de acceso a datos a una clase llamada ClientDAO (aplicando el patrón DAO) para encapsular todo el acceso al origen de datos, la conexión a la base de datos separarla con algún ORM como Hibernate u otro y/o crear una “fábrica de DAO’s”, la escritura del archivo por ejemplo en una clase llamada FileWriterService cuya única responsabilidad sea escribir los datos recibidos en un archivo json.

Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Clase Client solo con sus getters y setters
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Definiciones o contratos del DAO del cliente
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Implementación del contrato
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
FileWriterService class

Veamos otro ejemplo de la vida real donde aplicar el principio Single Responsibility

Principio Abierto/Cerrado (Open/Closed)

“Abierto a la extensión” significa que debemos diseñar clases para que se puedan agregar nuevas funcionalidades a medida que se van creando nuevos requerimientos. “Cerrado para modificación” significa que una vez que desarrolló una clase, nunca debe modificarse excepto para corregir errores. Parece complicado, pero no lo es tanto y la herencia/composición resuelve el problema, veámoslo en acción.

En el ejemplo anterior de forma intencional se agregó el principio abierto cerrado con las clases ClientDAO y ClientDAOImpl. En la interface ClientDAO agregamos los métodos que son obligatorios para controlar el acceso a datos del Cliente, además si ahora la empresa nos solicita que el software debe además poder insertar y modificar clientes podemos extender fácilmente la funcionalidad de la siguiente forma:

Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Extendemos la funcionalidad en nuestro DAO
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Agregamos la implementación a ClientDAOImpl

Veamos otro ejemplo con una violación del principio OCP

Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Clase que representa una operación de Suma en la calculadora
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Clase que representa una operación de Resta en la calculadora
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Clase que contiene el método para calcular según el tipo de operación

¿Qué pasaría si deseamos agregar la lógica para calcular multiplicaciones y divisiones?. Tendríamos que modificar la clase Calculator y agregar dicha lógica ahí, rompiendo con el principio Closed (cerrado para modificaciones). ¿Cómo lo solucionamos?

Sacando la lógica de ejecución de la operación

Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
calculateOperation será genérico para todas las operaciones
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Implementamos el método creado según la lógica de suma
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Implementamos el método creado según la lógica de resta
Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Probamos la implementación del código

Extendamos la funcionalidad a división

Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Implementamos el método creado según la lógica de división extendiendo desde la interfaz CalculatorOperation

Probamos nuevamente

Principios SOLID (Single Responsibility & Open/Closed) aplicados al desarrollo de software con Java
Resultado final de la adaptación del código al principio Open/Closed

De esta forma nuestro programa queda completamente abierto a nuevas funcionalidades pero cerrado a modificaciones de la implementación particular de cada clase.

En una próxima entrega ejemplificaré los 3 últimos principios: Liskov Substitution, Interface Segregation y Dependency Inversion

Visited 98 times, 1 visit(s) today

Deja un comentario