Acrónimo inventado por Robert C. Martin para establecer 5 principios básicos en la programación orientada a objetos.

El objetivo de tener un buen diseño de programación es abarcar la fase de mantenimiento de una manera más legible y sencilla, así como conseguir crear nuevas funcionalidades sin tener que modificar en gran medida código antiguo. Los costes de mantenimiento pueden abarcar el 80% de un proyecto de software, por lo que hay que valorar un buen diseño.[1]

S-Responsabilidad simple (Single responsibility)

Este principio trata de destinar cada clase a una finalidad sencilla y concreta. Esto también debe aplicarse para el caso de los métodos, en los que cada uno debería realizar una sola actividad.


Para el siguiente caso tenemos que la clase Vehículo, además de definir las características propias de la clase, posee una responsabilidad más: imprimir el objeto que se instancia. Esto puede ser un problema en el futuro. Imagine que por ahora dicho método imprime en el terminal. Sin embargo, en un futuro se planea que se pueda utilizar en un ambiente web y deba mostrarse utilizando etiquetas HTML, tendría que cambiar la estructura de la clase.

vehiculo
Fig 1. Vehículo

Lo más recomendable separar las responsabilidades con el fin de que cada clase posea una responsabilidad, por lo cual se quitaría de Vehículo el método “imprimir” y se trasladaría a una nueva clase denominada VehiculoImpresor.

VehiculoImpresor
Fig 2. VehículoImpresor

O-Abierto/Cerrado (Open/Closed)

Principio atribuido a Bertrand Meyer que habla de crear clases extensibles sin necesidad de entrar al código fuente a modificarlo [3]. Es decir, el diseño debe ser abierto para extenderse pero cerrado para modificarse. Lo complicado es predecir por donde se debe extender sin tener que modificarse. Para conseguir este principio hay que tener muy claro cómo va a funcionar la aplicación, por donde se puede extender y como van a interactuar las clases.

El uso más común de extensión es mediante la herencia y la reimplementación de métodos. Existe otra alternativa, que consiste en utilizar métodos que acepten una interface de manera que podemos ejecutar cualquier clase que implemente ese interface. En todos los casos, el comportamiento de la clase cambia sin que hayamos tenido que tocar código interno.


Siguiendo con el ejemplo anterior, si se debe imprimir en HTML el vehículo no sería correcto implementar un nuevo método en VehiculoImpresor, es posible definir una interfaz que permita ampliar en el tiempo las funcionalidades de cada clase que se va creando.

OpenClose
Fig 3. Interface para VehiculoImpresor

L-Sustitución Liskov (Liskov substitution)

Este principio fue creado por Barbara Liskov y habla de la importancia de originar todas las clases derivadas para que también puedan ser tratadas como la propia clase base. Cuando se crean clases derivadas, se debe asegurar de no re-implementar métodos que hagan que los métodos de la clase base no funcionarán si se tratan como un objeto de esa clase base.


Con el ejemplo de la Figura 3, con las clases VehiculoImpresorConsola y VehiculoImpresorHTML puede sustituirse sin problema con la interface IVehiculoImpresor.

I-Segregación del interface (Interface segregation)

Este principio fue formulado por Robert C. Martin y trata de algo parecido al primer principio. Cuando se definen interfaces, estos deben ser específicos a una finalidad concreta. Por ello, si tenemos que definir una serie de métodos abstractos que debe utilizar una clase a través de interfaces, es preferible tener muchas interfaces que definan pocos métodos que tener un interface con muchos métodos.

El objetivo de este principio es principalmente seguir aprovechando los interfaces en otras clases. Si tenemos un interface que compara y clona en el mismo interface, de manera más complicada se podrá utilizar en una clase que solo debe comparar o en otra que solo debe clonar.


En la figura 4, se establecen demasiadas responsabilidades para la interface ITrasporte, con el consiguiente problema que las clases que implementen dicha interfaz están obligados a implementar los tres métodos descritos. Lo cual es erróneo.

SegregacionInterface1
Fig 4. Interface con varias responsabilidades

Para solucionar esto se debe separar cada responsabilidad. Tal como la figura 5.

SegregacionInterface2
Fig 5. Interfaces separadas

D-Inversión de dependencias (Dependency inversion)

También fue definido por Robert C. Martin. El objetivo de este principio es conseguir desacoplar las clases. En todo diseño existe un acoplamiento, pero debe evitarse en la medida de lo posible. Un sistema no acoplado no hace nada, pero un sistema altamente acoplado es muy difícil de mantener.

El objetivo de este principio es el uso de abstracciones para conseguir que una clase interactúe con otras clases sin que las conozca directamente. Es decir, las clases de nivel superior no deben conocer las clases de nivel inferior. Dicho de otro modo, no debe conocer los detalles. Existen diferentes patrones como la inyección de dependencias o service locator que nos permiten invertir el control.


inversion-dependencia
Fig. 6 Dirección está fuertemente acoplada
inversion-dependencia2
Fig 7. Se logra la inversión de dependencia

Glosario

Acoplamiento: Grado de dependencia que tienen dos unidades de software. Si hablamos de funciones, el acoplamiento nos da una idea de lo dependientes que son dos funciones entre sí. Es decir, en qué grado una función puede hacer su trabajo sin la otra. Si hablo del más bajo nivel de acoplamiento cuando dos unidades de software son totalmente independientes.[2]

Un bajo acoplamiento permite:

Cohesión: La cohesión mide el costo del cambio dentro de un elemento. Un elemento es cohesivo a medida que cambia el elemento entero cuando el sistema necesita cambiar.[4]

Bibliografía

[1] http://www.genbetadev.com/paradigmas-de-programacion/solid-cinco-principios-basicos-de-diseno-de-clases

[2] http://latecladeescape.com/h/2015/07/acoplamiento-y-cohesion#

[3] https://devexperto.com/principio-open-closed/

[4] https://dosideas.com/noticias/java/502-acoplamiento-y-cohesion