De acuerdo con la documentación de Microsoft, el artículo principal de Wikipedia SOLID, o la mayoría de los arquitectos de TI, debemos asegurarnos de que cada clase tenga una sola responsabilidad. Me gustaría saber por qué, porque si todos parecen estar de acuerdo con esta regla, nadie parece estar de acuerdo con las razones de esta regla.
Algunos citan un mejor mantenimiento, otros dicen que proporciona pruebas fáciles o hace que la clase sea más robusta o de seguridad. ¿Qué es correcto y qué significa realmente? ¿Por qué mejora el mantenimiento, facilita las pruebas o hace que el código sea más robusto?
design-patterns
architecture
solid
single-responsibility
Bastien Vandamme
fuente
fuente
Respuestas:
Modularidad. Cualquier lenguaje decente le dará los medios para pegar piezas de código, pero no hay una forma general de pegar una gran pieza de código sin que el programador realice una cirugía en la fuente. Al agrupar muchas tareas en una construcción de código, se priva a usted mismo y a otros de la oportunidad de combinar sus piezas de otras maneras, e introduce dependencias innecesarias que podrían causar cambios en una pieza para afectar a las otras.
SRP es tan aplicable a las funciones como a las clases, pero los lenguajes OOP convencionales son relativamente pobres para pegar funciones juntas.
fuente
Un mejor mantenimiento, pruebas fáciles, corrección de errores más rápida son solo resultados (muy agradables) de la aplicación de SRP. La razón principal (como dice Robert C. Matin) es:
En otras palabras, SRP plantea cambio de localidad .
SRP también promueve el código DRY. Mientras tengamos clases que tengan una sola responsabilidad, podemos elegir usarlas en cualquier lugar que queramos. Si tenemos una clase que tiene dos responsabilidades, pero solo necesitamos una de ellas y la segunda interfiere, tenemos 2 opciones:
fuente
Es fácil crear código para solucionar un problema en particular. Es más complicado crear código que solucione ese problema al tiempo que permite realizar cambios posteriores de forma segura. SOLID proporciona un conjunto de prácticas que mejoran el código.
En cuanto a cuál es correcto: los tres. Todos son beneficios de usar responsabilidad única y la razón por la que debe usarla.
En cuanto a lo que significan:
Intente crear código por un tiempo siguiendo el principio, y vuelva a visitar ese código más adelante para hacer algunos cambios. Verá la gran ventaja que proporciona.
Usted hace lo mismo con cada clase y termina con más clases, todas cumpliendo con SRP. Hace que la estructura sea más compleja desde el punto de vista de las conexiones, pero la simplicidad de cada clase lo justifica.
fuente
Aquí están los argumentos que, en mi opinión, respaldan la afirmación de que el Principio de responsabilidad única es una buena práctica. También proporciono enlaces a más literatura, donde puedes leer razonamientos aún más detallados, y más elocuentes que los míos:
Mejor mantenimiento : idealmente, cada vez que una funcionalidad del sistema tiene que cambiar, habrá una única clase que debe cambiarse. Un mapeo claro entre clases y responsabilidades significa que cualquier desarrollador involucrado en el proyecto puede identificar de qué clase se trata. (Como ha señalado @ MaciejChałapuk, vea Robert C. Martin, "Código limpio" ).
Pruebas más fáciles : idealmente, las clases deberían tener una interfaz pública lo más mínima posible, y las pruebas deberían abordar solo esta interfaz pública. Si no puede realizar la prueba con claridad, ya que muchas partes de su clase son privadas, esta es una señal clara de que su clase tiene demasiadas responsabilidades y que debe dividirla en subclases más pequeñas. Tenga en cuenta que esto también se aplica a los idiomas en los que no hay miembros de clase "públicos" o "privados"; tener una pequeña interfaz pública significa que es muy claro para el código del cliente qué partes de la clase se supone que debe usar. (Ver Kent Beck, "Desarrollo guiado por pruebas" para más detalles).
Código robusto : su código no fallará más o menos a menudo porque está bien escrito; pero, como todo código, su objetivo final no es comunicarse con la máquina , sino con otros desarrolladores (vea Kent Beck, "Patrones de implementación" , Capítulo 1.) Una base de código clara es más fácil de razonar, por lo que se introducirán menos errores , y pasará menos tiempo entre el descubrimiento de un error y su reparación.
fuente
Existen varias razones, pero la que me gusta es el enfoque utilizado por muchos de los primeros programas de UNIX: hacer una cosa bien. Es bastante difícil hacer eso con una cosa, y cada vez es más difícil cuanto más intentas hacer.
Otra razón es limitar y controlar los efectos secundarios. Me encantó mi abridor de puerta de cafetera combinada. Desafortunadamente, el café generalmente se desbordó cuando tuve visitas. Olvidé cerrar la puerta después de preparar café el otro día y alguien lo robó.
Desde el punto de vista psicológico, solo puede realizar un seguimiento de algunas cosas a la vez. Las estimaciones generales son siete más o menos dos. Si una clase hace varias cosas, debe realizar un seguimiento de todas ellas a la vez. Esto reduce su capacidad de rastrear lo que está haciendo. Si una clase hace tres cosas, y solo quieres una, puedes agotar tu capacidad de hacer un seguimiento de las cosas antes de hacer algo con la clase.
Hacer varias cosas aumenta la complejidad del código. Más allá del código más simple, el aumento de la complejidad aumenta la probabilidad de errores. Desde este punto de vista, desea que las clases sean lo más simples posible.
Probar una clase que hace una cosa es mucho más simple. No tiene que verificar que la segunda cosa que la clase hizo o no sucedió en cada prueba. Tampoco tiene que arreglar las condiciones rotas y volver a probar cuando una de estas pruebas falla.
fuente
Porque el software es orgánico. Los requisitos cambian constantemente, por lo que debe manipular los componentes con el menor dolor de cabeza posible. Al no seguir los principios de SOLID, puede terminar con una base de código que se establece en concreto.
Imagine una casa con un muro de hormigón con carga. ¿Qué sucede cuando eliminas este muro sin ningún tipo de apoyo? La casa probablemente se derrumbará. En el software no queremos esto, por lo que estructuramos las aplicaciones de tal manera que pueda mover / reemplazar / modificar fácilmente los componentes sin causar mucho daño.
fuente
Sigo el pensamiento:
1 Class = 1 Job
.Uso de una analogía de fisiología: motriz (sistema neural), respiración (pulmones), digestivo (estómago), olfatorio (observación), etc. Cada uno de estos tendrá un subconjunto de controladores, pero cada uno solo tiene una responsabilidad, ya sea administrar la forma en que funciona cada uno de sus respectivos subsistemas o si son un subsistema de punto final y realizan solo 1 tarea, como levantar un dedo o hacer crecer un folículo piloso.
No confunda el hecho de que puede estar actuando como gerente en lugar de como trabajador. Algunos trabajadores eventualmente son promovidos a Gerente, cuando el trabajo que realizaban se ha vuelto demasiado complicado para que un proceso lo maneje solo.
La parte más complicada de lo que he experimentado es saber cuándo designar una Clase como un proceso de Operador, Supervisor o Gerente. En cualquier caso, deberá observar y denotar su funcionalidad para 1 responsabilidad (Operador, Supervisor o Gerente).
Cuando una clase / objeto realiza más de 1 de estos roles de tipo, encontrará que el proceso general comenzará a tener problemas de rendimiento, o procesará cuellos de botella.
fuente
Lung
clase, yo postularía que una instanciaPerson
aún deberíaBreathe
, y por lo tanto laPerson
clase debería contener suficiente lógica para, como mínimo, delegar las responsabilidades asociadas con ese método. Sugeriría además que un bosque de entidades interconectadas a las que solo se puede acceder a través de un propietario común a menudo es más fácil de razonar que un bosque que tiene múltiples puntos de acceso independientes.Person
clase tiene como función delegar toda la funcionalidad asociada con una entidad monstruosamente complicada de "ser humano del mundo real", incluida la asociación de sus diversas partes. . Tener una entidad que haga 500 cosas es feo, pero si esa es la forma en que funciona el sistema del mundo real que se está modelando, tener una clase que delegue 500 funciones mientras conserva una identidad puede ser mejor que tener que hacer todo con piezas disjuntas.Person
pero aún así informa de la cadena sobre el éxito / el fracaso, pero no necesariamente afecta al otro. 500 funciones (aunque establecido como ejemplo, sería exagerado y no soportable) Intento mantener ese tipo de funcionalidad derivada y modular.Fred.vision.lookAt(George)
tiene sentido, pero permitiendo que el código que decirsomeEyes = Fred.vision; someTubes = Fred.digestion
que parece repulsivo ya que oscurece la relación entresomeEyes
ysomeTubes
.Especialmente con un principio tan importante como la responsabilidad única, personalmente esperaría que haya muchas razones por las cuales las personas adoptan este principio.
Algunas de esas razones pueden ser:
También tenga en cuenta que SRP viene con un paquete de principios SÓLIDOS. Cumplir con SRP e ignorar el resto es tan malo como no hacer SRP en primer lugar. Por lo tanto, no debe evaluar SRP por sí mismo, sino en el contexto de todos los principios SÓLIDOS.
fuente
... test is not affected by other responsibilities the class has
, ¿podría por favor elaborar sobre este?La mejor manera de comprender la importancia de estos principios es tener la necesidad.
Cuando era un programador novato, no pensaba mucho en el diseño, de hecho, ni siquiera sabía que existían patrones de diseño. A medida que crecían mis programas, cambiar una cosa significaba cambiar muchas otras. Fue difícil rastrear errores, el código era enorme y repetitivo. No había mucha jerarquía de objetos, las cosas estaban por todas partes. Agregar algo nuevo o eliminar algo viejo generaría errores en las otras partes del programa. Imagínate.
En proyectos pequeños, puede que no importe, pero en proyectos grandes, las cosas pueden ser muy pesadillas. Más tarde, cuando me topé con los conceptos de patrones de diseño, me dije a mí mismo: "Oh, sí, hacer esto hubiera hecho las cosas mucho más fáciles".
Realmente no puede comprender la importancia de los patrones de diseño hasta que surja la necesidad. Respeto los patrones porque, por experiencia, puedo decir que hacen que el mantenimiento del código sea fácil y robusto.
Sin embargo, al igual que usted, todavía no estoy seguro acerca de las "pruebas fáciles", porque todavía no he tenido la necesidad de entrar en las pruebas unitarias.
fuente
La respuesta es, como han señalado otros, todos son correctos, y todos se alimentan entre sí, más fácil de probar hace que el mantenimiento sea más fácil, hace que el código sea más robusto, hace que el mantenimiento sea más fácil ...
Todo esto se reduce a un principal clave: el código debe ser tan pequeño y hacer tan poco como sea necesario para hacer el trabajo. Esto se aplica a una aplicación, un archivo o una clase tanto como a una función. Cuantas más cosas haga un fragmento de código, más difícil será comprenderlo, mantenerlo, extenderlo o probarlo.
Creo que se puede resumir en una palabra: alcance. Preste mucha atención al alcance de los artefactos, cuanto menos alcance haya en un punto en particular en una aplicación, mejor.
Alcance ampliado = más complejidad = más formas de que las cosas salgan mal.
fuente
Si una clase es demasiado grande, se vuelve difícil de mantener, evaluar y comprender, otras respuestas han cubierto esta voluntad.
Es posible que una clase tenga más de una responsabilidad sin problemas, pero pronto tendrá problemas con clases demasiado complejas.
Sin embargo, tener una regla simple de "solo una responsabilidad" hace que sea más fácil saber cuándo necesita una nueva clase .
Sin embargo, definir "responsabilidad" es difícil, no significa "hacer todo lo que dice la especificación de la aplicación", la verdadera habilidad es saber cómo dividir el problema en pequeñas unidades de "responsabilidad".
fuente