🧩 Single Responsibility Principle (SRP) - Principio de Responsabilidad Única
¿Qué es?
El Single Responsibility Principle es uno de los cinco principios SOLID de la programación orientada a objetos. Este principio dice que una clase debe tener una única responsabilidad y por lo tanto solo una razón para cambiar.
La frase original de Robert C. Martin, el tío Bob, que fue quien formuló este principio, es:
“A class should have one, and only one, reason to change.”

¿De dónde sale este principio?
Uncle Bob introdujo el SRP como la primera letra del acrónimo SOLID en su libro “Agile Software Development, Principles, Patterns, and Practices” (2002). Pero ojito, que la idea no se la inventó de cero. Está muy inspirada en el concepto de cohesión que ya describían Tom DeMarco y Meilir Page-Jones en los años 70 y 80 dentro de la ingeniería de software estructurada, gracias Wikipedia.
La idea de cohesión venía a decir que un módulo debe agrupar funcionalidades que estén relacionadas entre sí. Uncle Bob lo llevó un paso más allá y lo simplificó en una regla muy clara: una sola razón para cambiar.
Con el tiempo, el propio Uncle Bob refinó la definición del principio. En su libro posterior, “Clean Architecture” (2017), lo reformuló así:
“A module should be responsible to one, and only one, actor.”
Es decir, no se trata solo de hacer una sola cosa, sino de que un solo grupo de personas o stakeholder sea quien motive los cambios en esa clase. Si tu clase cambia porque lo pide el equipo de contabilidad y también porque lo pide el equipo de marketing, tienes dos razones para cambiar. Eso incumple el SRP.
¿Por qué es tan importante?
Aplicar el SRP facilita muchas cosas que en el día a día del desarrollo se agradecen bastante.
- Mantenibilidad: Clases pequeñas y enfocadas son mucho más fáciles de modificar sin romper nada.
- Legibilidad: Abres una clase y en 5 segundos sabes qué hace. No necesitas leer 500 líneas para entenderla.
- Testabilidad: Si una clase hace una sola cosa, testearla es directo. No necesitas mockear medio universo.
- Reutilización: Una clase con una responsabilidad clara se puede reutilizar en otros contextos sin arrastrar cosas innecesarias.
- Trabajo en equipo: Menos conflictos en Git. Si cada clase tiene su responsabilidad, es menos probable que dos personas toquen el mismo fichero a la vez.
Ejemplo
Imagina que trabajas en una tienda muy grande como un empleado que lo hace todo. Repones productos en las estanterías, atiendes a los clientes, cobras en la caja, revisas el stock, haces pedidos, cuadras las cuentas, contactas con distribuidores…
Es bastante probable que exista un empleado con todas estas tareas, quizá en tiendas pequeñas, pero en una tienda grande la tendencia es distribuir las tareas para ganar eficiencia y organización. Por ejemplo:
- Cajero/a — cobra en la caja
- Reponedor/a — repone productos
- Vendedor/a — atiende a los clientes
Repartir las tareas de esta forma también hace que las personas cometan menos fallos, porque se especializan en sus tareas y las hacen bien.
En la programación pasa algo similar. Si tienes una clase que hace de todo, como tu rol en la tienda, al final se vuelve un punto de fallo único y puede llegar a ser un auténtico caos.
Si algo va mal o hay que cambiar algo, todo el sistema se ve afectado y vulnerable a posibles fallos. Corregir un pequeño problema puede volverse complicado y costoso.
Por eso tu clase SuperDependiente no debería implementar los métodos Atender, Reponer, Cobrar, CuadrarCuentas, RevisarStock… quedaría algo así:
class SuperDependiente {
public function atenderCliente() { /* ... */ }
public function reponerProductos() { /* ... */ }
public function cobrar() { /* ... */ }
public function revisarStock() { /* ... */ }
public function hacerPedidos() { /* ... */ }
public function cuadrarCuentas() { /* ... */ }
}
$empleadoEjemplar = new SuperDependiente();
Esta clase tiene seis razones para cambiar. Si cambia la lógica de cobro, tocas esta clase. Si cambia el proceso de reposición, tocas la misma clase. Cualquier cambio en cualquier área afecta a la misma clase, y eso es una bomba nena.
Refactor
Si refactorizamos para cumplir el SRP quedaría algo así:
class Vendedor {
public function atenderCliente() { /* ... */ }
}
class Reponedor {
public function reponerProductos() { /* ... */ }
}
class Cajero {
public function cobrar() { /* ... */ }
}
class GestorInventario {
public function revisarStock() { /* ... */ }
public function hacerPedidos() { /* ... */ }
}
class Contable {
public function cuadrarCuentas() { /* ... */ }
}
Ahora cada clase tiene una sola razón para existir. Si mañana cambia la forma de cobrar, solo tocas Cajero. Si cambia la lógica de inventario, solo tocas GestorInventario. El resto del sistema permanece intacto.
Con este refactor ganamos:
- Mantenibilidad: Es más fácil mantener clases con una sola responsabilidad.
- Legibilidad: Código más fácil de entender.
- Testabilidad: Código más fácil de testear.
- Menor riesgo: Un cambio en una zona no rompe otra zona completamente diferente.
Un ejemplo más real: la clase User
El ejemplo de la tienda es muy visual, pero vamos a ver uno más cercano al código del día a día. Imagina una clase User típica:
class User {
public function getName(): string { /* ... */ }
public function getEmail(): string { /* ... */ }
// Persistencia
public function save(): void { /* guarda en base de datos */ }
// Notificaciones
public function sendWelcomeEmail(): void { /* envía un email */ }
// Validación
public function validate(): bool { /* valida los datos */ }
// Formateo
public function toJson(): string { /* convierte a JSON */ }
}
Esta clase tiene cuatro responsabilidades mezcladas: datos del usuario, persistencia, notificaciones y serialización. Cuatro razones para cambiar.
El refactor sería:
class User {
public function __construct(
private string $name,
private string $email,
) {}
public function getName(): string { return $this->name; }
public function getEmail(): string { return $this->email; }
}
class UserRepository {
public function save(User $user): void { /* guarda en base de datos */ }
}
class UserNotifier {
public function sendWelcomeEmail(User $user): void { /* envía email */ }
}
class UserValidator {
public function validate(User $user): bool { /* valida datos */ }
}
class UserSerializer {
public function toJson(User $user): string { /* convierte a JSON */ }
}
Ahora User solo se encarga de sus datos. La persistencia, las notificaciones, la validación y el formateo están cada uno en su sitio. Limpio.
¿Cómo detectar que estás violando el SRP?
Hay varias señales de alarma que te avisan de que una clase está haciendo demasiado:
- Por el nombre: Nombres con “And”, “Or” o múltiples verbos (
validateAndProcess,SaveAndNotify) se delatan solos. - Clases grandes: Si una clase tiene más de 10-15 métodos, probablemente tiene múltiples responsabilidades.
- Muchas dependencias: Si inyectas 5 o más dependencias en el constructor, posiblemente la clase está haciendo demasiado.
- Difícil de testear: Si necesitas mockear muchas cosas para un solo test, mal asunto.
- Cambios frecuentes por razones distintas: Si tocas la clase cuando cambia la base de datos y también cuando cambia el diseño del email, tiene dos responsabilidades.
- Descripciones largas: Si al explicar lo que hace la clase necesitas usar la palabra “y” varias veces, está haciendo demasiado.
Errores comunes al aplicar el SRP
Ojo, que aplicar el SRP también tiene sus trampas:
- Sobre-fragmentar: Crear una clase por cada método no es el objetivo. El SRP dice “una responsabilidad”, no “un método”. Una clase
Cajeropuede tenercobrar(),emitirTicket()yabrirCaja()porque todo forma parte de la misma responsabilidad: gestionar el cobro. - Confundir responsabilidad con función: Una responsabilidad es un eje de cambio, no una función aislada. Si
cobrar()yemitirTicket()siempre cambian juntos por las mismas razones, pertenecen a la misma responsabilidad. - Crear clases “anémicas”: Clases que solo tienen getters y setters sin ninguna lógica. Eso tampoco es SRP, es no poner lógica en ningún sitio.
Conclusión
El SRP es probablemente el principio SOLID más fácil de entender pero uno de los más difíciles de aplicar bien en la práctica. La clave está en preguntarte siempre: “¿Cuántas razones tiene esta clase para cambiar?”. Si la respuesta es más de una toca trabajar y refactorizar.
No se trata de crear 200 clases con un método cada una. Se trata de que cada clase tenga una responsabilidad clara y bien definida, de forma que cuando algo cambie, sepas exactamente dónde ir a tocarlo sin miedo a cargarte nada más.
Espero que se haya entendido ejeejjeje EA nos beermos! 🍻
Pon a prueba lo aprendido
1. ¿Qué dice exactamente el Principio de Responsabilidad Única?
2. ¿Quién formuló originalmente el SRP?
3. En el ejemplo de la tienda, ¿por qué la clase SuperDependiente viola el SRP?
4. ¿Cuál de estas señales NO indica una violación del SRP?
5. ¿Cómo reformuló Uncle Bob el SRP en 'Clean Architecture' (2017)?