SRP: El Principio de Responsabilidad Única
Un módulo debe ser responsable ante un solo actor, evitando que los cambios de un departamento rompan accidentalmente otro.
Por qué importa
El SRP se malinterpreta comúnmente como "un módulo debe hacer solo una cosa". La definición real, como aclara Robert Martin en Clean Architecture, es más precisa: un módulo debe ser responsable ante uno, y solo uno, actor. Un actor es un grupo de personas — un departamento, un rol — cuyas exigencias impulsan cambios en ese módulo.
El ejemplo canónico es una clase Employee con tres métodos: calculatePay() solicitado por el CFO, reportHours() solicitado por el COO, y save() solicitado por el CTO. Cuando estos tres métodos comparten un helper privado — digamos, _regularHours() — el equipo del CFO puede romper accidentalmente los informes del COO simplemente ajustando el algoritmo de cálculo de pago. Los dos actores ahora están acoplados involuntariamente a través de una clase compartida. La cohesión es la fuerza que mantiene juntas las cosas que cambian por la misma razón; el SRP es el principio que separa las cosas que cambian por razones diferentes.
✗El problema
Three actors — CFO, COO, CTO — all depend on one class. A change requested by one actor silently breaks the logic used by another.
Bad
class Employee:
def calculate_pay(self) -> float: # owned by CFO
hours = self._regular_hours()
return hours * self.hourly_rate
def report_hours(self) -> float: # owned by COO
hours = self._regular_hours() # reuses same helper — coupling!
return hours
def save(self) -> None: # owned by CTO / DBA
db.save(self)
def _regular_hours(self) -> float: # shared — dangerous
return self.total_hours - self.overtime_hours
class Employee {
calculatePay(): number { // CFO's concern
return this.regularHours() * this.hourlyRate;
}
reportHours(): number { // COO's concern
return this.regularHours(); // accidental coupling!
}
save(): void { // CTO's concern
db.save(this);
}
private regularHours(): number { // shared — fragile
return this.totalHours - this.overtimeHours;
}
}
✓La solución
Three separate classes, one actor each. The Employee data structure holds state only — it has no behavior. Each class owns its own helper logic.
Good
class Employee:
"""Data structure only — no behavior."""
total_hours: float
overtime_hours: float
hourly_rate: float
class PayCalculator: # answers to CFO
def calculate_pay(self, emp: Employee) -> float:
return self._regular_hours(emp) * emp.hourly_rate
def _regular_hours(self, emp: Employee) -> float:
return emp.total_hours - emp.overtime_hours
class HourReporter: # answers to COO
def report_hours(self, emp: Employee) -> float:
return emp.total_hours # uses its own definition
class EmployeeRepository: # answers to CTO / DBA
def save(self, emp: Employee) -> None:
db.save(emp)
interface Employee {
totalHours: number;
overtimeHours: number;
hourlyRate: number;
}
class PayCalculator { // answers to CFO
calculatePay(emp: Employee): number {
return this.regularHours(emp) * emp.hourlyRate;
}
private regularHours(emp: Employee): number {
return emp.totalHours - emp.overtimeHours;
}
}
class HourReporter { // answers to COO
reportHours(emp: Employee): number {
return emp.totalHours; // owns its own definition
}
}
class EmployeeRepository { // answers to CTO
save(emp: Employee): void {
db.save(emp);
}
}
💡Conclusión clave
El SRP no se trata de hacer una sola cosa — se trata de servir a un solo actor. Si dos ejecutivos diferentes pueden solicitar cambios conflictivos al mismo módulo, ese módulo está violando el SRP y es una bomba de tiempo para el acoplamiento accidental.
🔧 Algunos ejercicios pueden tener errores. Si algo parece incorrecto, usa el botón Feedback (abajo a la derecha) para reportarlo — nos ayuda a corregirlo rápido.
Pista: Si distintos ejecutivos pueden solicitar cambios conflictivos en la misma clase, sirve a múltiples actores — sepárala.
✗ Tu versión