Principios SOLID: Principio de Responsabilidad Única.
El primero de los cinco principios SOLID es el Principio de Responsabilidad Única pero ¿sabemos realmente que significa?, ¿sabemos como se lleva a cabo o cuando o como aplicarlo?, ¿entendemos realmente a qué se está refiriendo cuando se habla de una única responsabilidad? y yendo un poco más allá, ¿qué es una responsabilidad?
Según la Wikipedia, la definición de SOLID es la siguiente:
En ingeniería de software, SOLID (Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion) es un acrónimo mnemónico introducido por Robert C. Martin a comienzos de la década del 2000 que representa cinco principios básicos de la programación orientada a objetos y el diseño.
SRP: The Single-Responsability Principle
El Principio de Responsabilidad Única (Single-Responsibility Principle) es el primero de estos cinco principios. Fue descrito por primera vez en un trabajo de Tom DeMarco y Meilir Page-Jones, refiriéndose a él como cohesión y lo definieron como la relación funcional de los elementos de un módulo. Aunque, lo que es el concepto de cohesión como tal fue Larry Constantine el que lo inventó.
En el contexto del Principio de Responsabilidad Única, se define responsabilidad como:
Una clase debería tener una única razón para cambiar.
Es interesante destacar el cambio de visión que se hace con la frase anterior, ya que hemos pasado de pensar en una clase que tiene que tener una única responsabilidad a pensar en que una clase tiene que tener una única razón para cambiar, así que ¿son estos conceptos similares? ¿es lo mismo? ¿podríamos afirmar que sería correcto en base a este principio que una clase tuviera más de una responsabilidad siempre y cuando tuviera una única razón para cambiar? Sigamos.
El Principio de Responsabilidad Única nos guía a separar los comportamientos basándonos en ejes del cambio. Podríamos decir que, y aquí está la clave de todo, cada responsabilidad es un eje del cambio si y solo si el cambio ocurre. Esto significa que si tuviéramos una clase con tres responsabilidades invariantes en su comportamiento, no estaríamos incurriendo en una violación del SRP. De hecho, tal y como indica Robert C. Martin en su libro Agile Software Development, ni este principio ni cualquier otro, debería aplicarse si no hay síntoma. Si alguna de esas tres responsabilidades definidas por el comportamiento cambiaran en distintos momentos en el tiempo, es decir, se volvieran variantes, entonces si que deberíamos separar dichas responsabilidades en otras clases ya que tendríamos el síntoma de tener una clase con varios ejes del cambio.
Si puedes pensar en más de un motivo para cambiar una clase en momentos diferentes, entonces, esa clase tiene más de una responsabilidad y si una clase tienen más de una responsabilidad, entonces las responsabilidades están acopladas por lo que los cambios en una de esas responsabilidades pueden perjudicar la capacidad de dicha clase para cumplir con otras. Este tipo de acoplamiento nos llevan a diseños frágiles que luego producen errores y problemas inesperados cuando son cambiados.
Como defender el Principio de Responsabilidad Única
Para defender el Principio de Responsabilidad Única en nuestro código podemos apoyarnos en mejorar dos aspectos del mismo. Estos son la cohesión y el acoplamiento, de forma que:
- Mantener una alta cohesión, es decir, mantener ‘unidas’ funcionalidades que estén relacionadas entre sí y mantener fuera aquello que no esté relacionado. El objetivo es aumentar la cohesión entre las cosas que cambian por las mismas razones.
- Mantener un bajo acoplamiento, es decir, reducir al máximo posible el grado de la relación de un clase o módulo con el resto, para favorecer crear código más fácilmente mantenible, extensible y testeable. El objetivo es disminuir el acoplamiento entre aquellas cosas que cambian de forma diferente.
Estos dos aspectos favorecerá un código con funcionalidades más concretas y mejor especificadas.
Reúne las cosas que cambian por las mismas razones. Separa las cosas que cambian por diferentes razones.
Detectar violaciones del Principio de Responsabilidad Única
Existen varias heurísticas que nos pueden dar una idea de si estamos violando este principio o no:
- Clases que mezclan o involucran varias capas de arquitectura, como por ejemplo mezclar presentación con lógica de negocio o a esta última con persistencia.
- Número alto de métodos públicos. Un clase con muchos métodos públicos puede darnos una idea del nivel de operaciones que realiza o de un amplio abanico de comportamientos.
- Número de imports. Si una clase necesita de muchas clases externas puede significar que tiene muchas responsabilidades y está intentando hacer muchas cosas para las cuales necesita a otras clases.
- Una nueva funcionalidad afecta siempre a una clase. Si cada vez que introducimos una nueva característica una clase se ve afectada, lo más normal es que esa clase haga muchas cosas relacionadas con muchas características.
- Métodos con parámetros que indiquen opciones. Suele pasar que si un método recibe un parámetro booleano casi siempre suele significar que dentro hay al menos un ‘if’ y un ‘else’ que hacen cosas distintas por lo que tendremos a un método haciendo más de una cosa.
En contra del Principio de Responsabilidad Única.
Al mismo tiempo que buscaba información sobre este principio, me he encontrado con páginas en las que se le ponía en jaque. Más o menos y de forma muy resumida y agrupada, las razones que daban eran la siguientes:
- Poco claro
En realidad no hay una explicación clara de lo que es ‘una razón del cambio’. El SRP habla de tener una única razón de cambios pero, ¿corregir un bug no es una buena razón de cambio?, ¿y una refactorización o una modificación para mejorar el rendimiento?
- Ambiguo
Incluso si nos referimos a una sola razón para el cambio. No hay un concepto de una razón buena o mala, de manera que ¿podríamos concentrar un motor de búsqueda completo en una clase porque podemos afirmar que que su responsabilidad es buscar documentos relevantes? o por el contrario ¿podríamos tener clases con un único método pensando en que es su única responsabilidad? Estos casos parecen permitidos por el principio…o no, depende de como se interprete. El principio no funcionará a menos que lleguemos a una idea común de que es una única responsabilidad.
- Arbitrario
El principio es arbitrario en si mismo ya que ¿qué es lo que hace que una y solo una razón de cambio sea mejor que dos razones de cambio?
- Desequilibrado
No hay equilibrio, todos los ejemplos que se suelen mostrar son unidireccionales para simplemente crear muchas clases de método único. No hay forma o ejemplo en el que se fusionen dos clases en una sola.
Esto es todo, espero que haya quedado un poco más claro este principio SOLID. Confieso que he tenido que leer bastante sobre el tema y revisar libros y artículos porque yo era el primero que no tenía muy claro su significado real a pesar de repetir su utilidad durante mucho tiempo. En el próximo artículo trataré el siguiente principio: El principio Abierto-Cerrado.
Como siempre, cualquier aclaración, corrección o propuesta de mejora será bienvenida.
Chimpún.
Bibliografía
Agile Software Development, Robert C. Martin.