El Single Responsibility Principle (SRP) o Principio de Responsabilidad Única estable que "una clase debe tener una, y solo una, razón para cambiar".
Reproducir video explicativo
En términos más simples, cada clase o módulo debería ser responsable de una parte específica de la funcionalidad, y esa responsabilidad debería estar completamente encapsulada por la clase.
# ¿Responsabilidad?
Al hablar de responsabilidad, hablamos de roles, de interesados en un módulo o función, actores que requieren cambios al software. Las responsabilidades son básicamente familias de funciones que cumplen las necesidades de dichos actores.
Cuando alguno de los roles involucrados en dichas responsabilidades decida cambiar o agregar funcionalidad, va a tener que cambiar dicha clase. Aumentando la probabilidad de colisión, complejidad y posibles bugs.
En el siguiente ejemplo tenemos una clase que afecta a varios actores, varias responsabilidades:
class Employee { public function calculatePay() { // ... // Contabilidad } public function save() { // ... // IT } public function describeEmployee() { // ... // Recursos Humanos } }
Por un lado contabilidad, por otro IT y por otro RRHH. Un cambio requerido por uno de ellos, va a afectar a los otros.
# ¿Por qué es importante?
La desventaja de que una clase o módulo tenga más de una responsabilidad, o mejor dicho, que tenga más de una razón para cambiar, es que cuando se introduce un cambio para alguna de las responsabilidades, puede afectar el funcionamiento de otras.
Mantenibilidad
Es más fácil mantener y modificar el código.
Flexibilidad
Puedes cambiar una parte del sistema sin afectar a otras.
Comprensibilidad
Cada parte del sistema se puede entender de manera aislada.
# Síntomas de que no se cumple
Podemos identificar cuándo no estamos respetando el SRP:
- Clase con muchas líneas de código.
- Cada vez que hay que se hace un cambio o se agrega una funcionalidad, es necesario tocar en muchos lugares.
- Mezcla de funcionalidades de distintas capas de arquitectura.
- "Tocar una parte" y que se "rompa otra cosa" del mismo módulo o clase.
Ejemplo
Supongamos que tenemos una aplicación para gestionar productos en una tienda.
Sin Single Responsibility Principle:
Una mala práctica sería tener una clase que maneje tanto las operaciones de la base de datos como la lógica de negocio de los productos.
public class ProductManager { public void addProduct(Product product) { // Lógica para agregar producto a la base de datos } public void calculateTotalStockValue() { // Lógica para calcular el valor total del inventario } }
// Aquí, ProductManager tiene múltiples razones para cambiar, lo que incumple el SRP.
Con Single Responsibility Principle:
Vamos a separar las responsabilidades en dos clases distintas: una que maneje la interacción con la base de datos y otra que maneje los cálculos del inventario.
// ProductDB.java
public class ProductDB { public void addProduct(Product product) { // Solo maneja la lógica de la base de datos para agregar productos } }
// InventoryCalculator.java
public class InventoryCalculator { public double calculateTotalStockValue() { // Solo maneja la lógica para calcular el valor del inventario // Retorna el valor calculado return 0.0; } }
// Product.java (Entidad)
public class Product { // Atributos como nombre, precio, cantidad, etc. private String name; private double price; private int quantity; // Constructor, getters y setters public Product(String name, double price, int quantity) { this.name = name; this.price = price; this.quantity = quantity; } // ... (otros métodos y atributos) }
Conclusiones
Al implementar el SRP, ProductDB se ocupa
exclusivamente de la interacción con la base de datos, mientras que InventoryCalculator
maneja solo los cálculos relacionados con el inventario.
Esto no solo hace que el código sea más fácil de mantener y extender, sino que también ayuda a evitar errores complicados que pueden surgir cuando una clase tiene demasiadas responsabilidades.
Resumen
Cuando se habla de una sola responsabilidad, no quiere decir que una clase tenga un sólo método, ni un módulo que tenga una sola clase, sino que incluya toda la funcionalidad relacionada que pueda cambiar junta, que dependa de un mismo actor.
Una correcta aplicación del principio de responsabilidad única hace que nuestro código sea flexible al cambio y lo vuelve menos propenso a errores.