¿Cómo determinar si una clase cumple con el principio de responsabilidad única?

34

El principio de responsabilidad única se basa en el principio de alta cohesión. La diferencia entre los dos es que una clase altamente cohesionada presenta un conjunto de responsabilidades que están fuertemente relacionadas, mientras que las clases que se adhieren al SRP tienen una sola responsabilidad.

Pero, ¿cómo determinamos si una clase en particular presenta un conjunto de responsabilidades y, por lo tanto, es muy coherente, o si solo tiene una responsabilidad y se adhiere al SRP? En otras palabras, ¿no es más o menos subjetivo, ya que algunos pueden considerar una clase muy granular (y como tal creerán que la clase se adhiere al SRP), mientras que otros pueden considerar que no es lo suficientemente granular?

usuario1483278
fuente

Respuestas:

21

Por qué sí, es muy subjetivo, y es el tema de muchos debates acalorados y enrojecidos en los que se encuentran los programadores.

Realmente no hay una sola respuesta, y la respuesta puede cambiar a medida que su software se vuelve más complejo. Lo que una vez fue una tarea bien definida, eventualmente puede convertirse en múltiples tareas mal definidas. Ese es siempre el problema también. ¿Cómo elige la forma correcta de dividir un programa en tareas?

El único consejo que puedo dar es este: use su mejor juicio (y el de sus compañeros de trabajo). Y recuerde que los errores pueden (generalmente) corregirse si los detecta lo suficientemente pronto.

Jason Baker
fuente
Desearía que la informática se pareciera más a la ciencia real. La subjetividad no tiene lugar en la ciencia real. Si bien los principios SÓLIDOS están bien por derecho propio, deben repetirse para minimizar la subjetividad y maximizar la objetividad. Eso aún no ha sucedido, lo que me hace cuestionar su legitimidad en el mundo real.
DarkNeuron
13

Bob Martin (tío Bob), quien originó los principios SÓLIDOS de los cuales SRP es el primero, dice acerca de esto (estoy parafraseando, no puedo recordar las palabras reales):

Una clase solo debe tener una razón para cambiar

Si tiene más de una razón, no se adhiere a SRP.

Oded
fuente
14
Eso es solo repetir la definición realmente, pero adherirse a srp sigue siendo bastante subjetivo.
Andy
7

Puedo darte varias reglas generales.

  • ¿Qué tan fácil es nombrar la clase? Si una clase es difícil de nombrar, probablemente esté haciendo demasiado.
  • ¿Cuántos métodos públicos tiene la clase? 7 +/- 2 es una buena regla general. Si la clase tiene más que eso, debería pensar en dividirla en varias clases.
  • ¿Existen grupos cohesivos de métodos públicos utilizados en contextos separados?
  • ¿Cuántos métodos privados o miembros de datos hay? Si la clase tiene una estructura interna compleja, probablemente debería refactorizarla para que las partes internas se empaqueten en clases más pequeñas separadas.
  • Y la regla general más fácil: ¿qué tan grande es la clase? Si tiene un archivo de encabezado C ++ que contiene una sola clase que tiene más de un par de cientos de líneas, probablemente debería dividirla.
Dima
fuente
2
Con respecto a su segundo punto, visite uxmyths.com/post/931925744/…
Cameron Martin
77
No estamos de acuerdo con respecto a 7 +/- 2: el principio de responsabilidad única se trata de la cohesión semántica, no de números arbitrarios.
JacquesB
1
La regla de oro no necesita pruebas científicas independientes. El método científico moderno tiene siglos de antigüedad, la arquitectura y la ingeniería tienen milenios de antigüedad. La regla general para los métodos públicos es "varios" y ninguno de los parámetros es "unos pocos". En otras noticias, a pesar de que los dibujos de algunos niños muestran lo contrario, los brazos de las personas no salen de sus cabezas [cita requerida].
abuzittin gillifirca
@CameronMartin Dependiendo de su configuración, la interfaz para una clase puede o no estar fácilmente disponible para que la lea. Buscar una interfaz de usuario no es lo mismo que escribir código: si tengo que consultar la documentación cada minuto, al menos estoy duplicando el tiempo que lleva hacer un trabajo real.
Más claro
6

Los Principios de responsabilidad única dicen que cada módulo de software debe tener una sola razón para cambiar. En un artículo reciente, el tío Bob explicó "razones para cambiar",

Sin embargo, al pensar en este principio, recuerde que las razones del cambio son las personas. Son las personas las que solicitan cambios. Y no quiere confundir a esas personas, ni a usted mismo, mezclando el código que a muchas personas les importa por diferentes razones.

Explicó además el concepto con un ejemplo AQUÍ .

El d
fuente
Ese es un gran artículo, escrito por el hombre mismo.
MrDustpan
4

Para responder a esto, dé un paso atrás y considere la intención del principio de responsabilidad única. ¿Por qué es un principio de diseño recomendado en primer lugar?

El propósito del principio es "compartimentar" la base del código, de modo que el código relacionado con una única "responsabilidad" está aislado en una sola unidad. Esto facilita la búsqueda y comprensión del código y, lo que es más importante, significa que los cambios en la "responsabilidad" solo afectarán a una sola unidad de código.

Lo que nunca quiere en un sistema es cuando una pequeña posibilidad hace que otra parte aparentemente no relacionada del código falle o cambie el comportamiento. El SRP ayuda a aislar errores y cambios.

Entonces, ¿qué es una "responsabilidad"? Es algo que podría cambiar independientemente de otros cambios. Supongamos que tiene un programa que puede guardar algunas configuraciones en un archivo de configuración XML y puede volver a leerlas desde el archivo. ¿Es esta una responsabilidad única o "cargar" y "guardar" dos responsabilidades diferentes? Cualquier tipo de cambio en el formato o estructura del archivo requeriría cambiar tanto la lógica de carga como la de guardar. Por lo tanto, es una responsabilidad única que debe estar representada por una sola clase. Ahora considere una aplicación que puede exportar algunos datos en formato CVS, Excel y XML. En este caso, es fácil imaginar que un formato podría cambiar sin afectar al otro. Si decide cambiar el delimitador en el formato CVS, no debería afectar la salida de Excel.

JacquesB
fuente
2

OO dice que las clases son una agrupación de datos y una funcionalidad. Esta definición deja mucho espacio para la interpretación subjetiva.

Sabemos que las clases deben definirse clara y fácilmente. Pero, para definir una clase de este tipo, debemos tener una idea clara de cómo una clase encaja en el diseño general. Sin requisitos de tipo cascada que, paradójicamente, se consideran un antipatrón ... esto es difícil de lograr.

Podemos implementar un diseño de clase con una arquitectura que funcione en la mayoría de los casos, como MVC. En las aplicaciones MVC solo asumimos que tenemos datos, una interfaz de usuario y un requisito para que los dos se comuniquen.

Con una arquitectura básica, es más fácil identificar casos en los que se rompen las reglas de responsabilidad individual. Por ejemplo, pasar una instancia de un Control de usuario a un modal.

P.Brian.Mackey
fuente
1

Solo por el bien de la discusión, mencionaré una clase de JUCE llamada AudioSampleBuffer . Ahora esta clase existe para contener un fragmento (o quizás un fragmento bastante largo) de audio. Sabe que la cantidad de canales, la cantidad de muestras (por canal), parece estar comprometida con el flotante IEEE de 32 bits en lugar de tener una representación numérica variable o un tamaño de palabras (pero eso no es un problema para mí). Hay funciones miembro que le permiten obtener numChannels o numSamples y punteros a cualquier canal en particular. Puede hacer que un AudioSampleBuffer sea más largo o más corto. Supongo que los primeros cero-pads el búfer, mientras que el último se trunca.

Hay algunos miembros privados de esta clase que se utilizan para asignar espacio en el montón especial que utiliza JUCE.

Pero esto es lo que falta AudioSampleBuffer (y he tenido varias discusiones con Jules al respecto): un miembro llamó SampleRate. ¿Cómo puede faltar eso?

La única responsabilidad que debe cumplir un AudioSampleBuffer es representar adecuadamente el audio físico que se escucha que representan sus muestras. Cuando ingresa un AudioSampleBuffer de algo que lee un archivo de sonido o de una transmisión, hay un parámetro adicional que debe obtener y pasarlo junto con el AudioSampleBuffer a los métodos de procesamiento (digamos que es un filtro) que necesita conocer la frecuencia de muestreo o, finalmente, a un método que reproduce el búfer para ser escuchado (o lo transmite a otro lugar). Lo que sea.

Pero lo que tiene que hacer es continuar pasando este SampleRate, que es inherente al audio específico que vive en AudioSampleBuffer, por todas partes. He visto código donde se pasó una constante 44100.0f a una función, porque el programador no parecía saber qué más hacer.

Este es un ejemplo de incumplimiento de su responsabilidad única.

robert bristow-johnson
fuente
1

Se puede hacer una forma concreta, en base a lo que usted dijo: que la alta cohesión conlleva una responsabilidad única que puede medir la cohesión. Una clase cohesiva máxima tiene todos los campos utilizados en todos los métodos. Si bien no siempre es posible ni deseable tener una clase de cohesión máxima, es mejor llegar a esto. Tener este objetivo de diseño de clase es bastante fácil deducir que su clase no puede tener muchos métodos o campos (algunos dicen que a lo sumo 7).

Otra forma es desde los conceptos básicos puros de OOP: modelo después de objetos reales. Es mucho más fácil ver la responsabilidad de los objetos reales. Sin embargo, si el objeto real es demasiado complejo, divídalo en múltiples objetos de contacto, cada uno de ellos tiene su propia responsabilidad.

m3th0dman
fuente