En este tutorial discutiremos los dos primeros principios SOLID del diseño orientado a objetos ejemplificando cada principio utilizando código 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:
Principio | Descripción |
---|---|
Single Responsibility | Una clase debería tener solo una responsabilidad y solo una razón para cambiar |
Open/Closed | Los componentes deberán estar abiertos para poder extender su funcionalidad pero cerrados para modificarlos |
Liskov Substitution | Las 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 B con A sin interrumpir el comportamiento de nuestro programa |
Interface Segregation | Los 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 Inversion | El 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. |
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:
La clase que escribamos tendría varias razones para cambiar:
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.
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:
Veamos otro ejemplo con una violación del principio OCP
¿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
Extendamos la funcionalidad a división
Probamos nuevamente
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
En este tutorial te mostraré cómo crear un sistema de login con Python Flask de…
Aprende a crear un mantenedor completo y funcional en este Tutorial de Flask con Python,…
¿Quieres aprender a subir imágenes a un servidor con Python Flask de manera fácil y…
Cuando se crea un proyecto Python mediante programación orientada a objetos (POO), una parte importante…
Navegando en Twitter sobre temas de programación y tecnología encontré esta guía para entrevistas técnicas…
En el mundo de la programación de software, surgen los microservicios como una innovación clave.…