Según Robert C. Martin, el SRP afirma que:
Nunca debe haber más de una razón para que una clase cambie.
Sin embargo, en su libro Clean Code , capítulo 3: Funciones, muestra el siguiente bloque de código:
public Money calculatePay(Employee e) throws InvalidEmployeeType {
switch (e.type) {
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
default:
throw new InvalidEmployeeType(e.type);
}
}
Y luego dice:
Hay varios problemas con esta función. Primero, es grande, y cuando se agreguen nuevos tipos de empleados, crecerá. Segundo, claramente hace más de una cosa. Tercero, viola el Principio de Responsabilidad Única (SRP) porque hay más de una razón para que cambie . [énfasis mío]
En primer lugar, pensé que el SRP estaba definido para las clases, pero resulta que también es aplicable a las funciones. En segundo lugar, ¿cómo es que esta función tiene más de una razón para cambiar ? Solo puedo verlo cambiar debido a un cambio en Empleado.
Respuestas:
Un detalle que a menudo se pierde del Principio de Responsabilidad Única es que las "razones para el cambio" están agrupadas por actores de casos de uso (puede ver una explicación completa aquí ).
Entonces, en su ejemplo, el
calculatePay
método deberá cambiarse siempre que se requieran nuevos tipos de Empleados. Dado que un tipo de empleado puede no tener nada que ver con otro, sería una violación del principio si los mantiene juntos, ya que el cambio afectaría a diferentes grupos de usuarios (o actores de casos de uso) en el sistema.Ahora, sobre si el principio se aplica a las funciones: incluso si tiene una violación en un solo método, todavía está cambiando una clase por más de una razón, por lo que sigue siendo una violación de SRP.
fuente
En la página 176, Capítulo 12: Emergencia, en la sección titulada Clases y métodos mínimos, el libro proporciona una especie de corrección al afirmar:
y
Obviamente, él está hablando del dogmatismo al seguir el SRP para desglosar pequeños métodos inocentes perfectamente bien como los
calculatePay()
anteriores.fuente
Cuando el Sr. Martin aplica el SRP a una función, está ampliando implícitamente su definición de SRP. Como el SRP es una redacción específica de OO de un principio general, y dado que es una buena idea cuando se aplica a las funciones, no veo ningún problema con eso (aunque podría haber sido bueno si lo hubiera incluido explícitamente en el definición).
Tampoco veo más de una razón para cambiar, y no creo que sea útil pensar en el SRP en términos de "responsabilidades" o "razones para cambiar". Esencialmente, a lo que se refiere el SRP es que las entidades de software (funciones, clases, etc.) deberían hacer una cosa y hacerlo bien.
Si echas un vistazo a mi definición, no es menos vago que la redacción habitual del SRP. El problema con las definiciones habituales del SRP no es que sean demasiado vagas, sino que intentan ser demasiado específicas sobre algo que es esencialmente vago.
Si observa lo que
calculatePay
hace, claramente está haciendo una sola cosa: despacho basado en el tipo. Dado que Java tiene formas integradas de hacer despachos basados en tipos,calculatePay
es poco elegante y no idiomático, por lo que debe reescribirse, pero no por las razones indicadas.fuente
Tienes razón @Enrique. No importa si es una función o un método de una clase, el SRP significa que en ese bloque de código solo hace una cosa.
La declaración 'razón para el cambio' a veces es un poco engañoso, pero si cambia
calculateSalariedPay
ocalculateHourlyPay
o la enumeración deEmployee.type
usted tiene que cambiar este método.En su ejemplo, la función:
En mi opinión, no es directamente una violación de SRP, ya que los casos de cambio y las llamadas no pueden escribirse más cortas, si piensa en Employee y los métodos ya existen. De todos modos, es una violación clara del principio abierto-cerrado (OCP), ya que debe agregar declaraciones de 'caso' si agrega tipos de empleados, por lo que es una mala implementación: refactorícela.
No sabemos cómo se debe calcular el 'Dinero', pero la forma más fácil es tener
Employee
como interfaz y algunas implementaciones concretas congetMoney
métodos. En ese caso, toda la función es innecesaria.Si es más complicado calcularlo, uno podría usar el patrón de visitante que tampoco es 100% SRP pero es más OCP que una caja de interruptor.
fuente