unica solid single responsibility responsabilidad principle principios principio para example ejemplos ejemplo dummies cerrado abierto oop solid-principles srp

oop - single - principios solid para dummies



¿Cómo defines una responsabilidad única? (13)

Sé que "la clase tiene una sola razón para cambiar". Ahora, ¿qué es eso exactamente? ¿Hay algunos olores / señales que podrían decir que la clase no tiene una sola responsabilidad? ¿O podría la respuesta real esconderse en YAGNI y solo refactorizarse a una sola responsabilidad la primera vez que su clase cambie?


Un método simple y práctico para verificar la responsabilidad individual (no solo las clases, sino también el método de clases) es la elección del nombre. Cuando diseñas una clase, si encuentras fácilmente un nombre para la clase que especifica exactamente lo que define, estás en el camino correcto. Una dificultad para elegir un nombre casi siempre es un síntoma de mal diseño.


Si encuentra problemas para ampliar la funcionalidad de la clase sin tener miedo de que pueda terminar rompiendo otra cosa, o no puede usar la clase sin modificar muchas de sus opciones que modifican su comportamiento, huele como si la clase hiciera demasiado.

Una vez que estaba trabajando con la clase heredada que tenía el método "ZipAndClean", que obviamente estaba comprimiendo y limpiando la carpeta especificada ...


Tal vez un poco más técnico que otros olores:

  • Si encuentra que necesita varias clases o funciones de "amigo", generalmente es un buen olor a mala SRP, ya que su clase no expone públicamente la funcionalidad requerida.
  • Si termina con una jerarquía excesivamente "profunda" (una larga lista de clases derivadas hasta llegar a las clases de hojas) o una jerarquía "amplia" (muchas, muchas clases derivadas superficialmente de una única clase principal). Por lo general, es una señal de que la clase de padres hace demasiado o muy poco. No hacer nada es el límite de eso, y sí, lo he visto en la práctica, con una definición de clase padre "vacía" solo para agrupar un grupo de clases no relacionadas en una sola jerarquía.

También encuentro que refactorizar a una sola responsabilidad es difícil. Para cuando finalmente lo hagas, las diferentes responsabilidades de la clase se habrán entrelazado en el código del cliente, lo que dificultará que se factorice una cosa sin romper la otra. Prefiero equivocarme del lado de "demasiado poco" que de "demasiado".


El Principio de Responsabilidad Individual

Hay muchos casos obvios, por ejemplo CoffeeAndSoupFactory . El café y la sopa en el mismo aparato pueden llevar a resultados bastante desagradables. En este ejemplo, el dispositivo podría estar dividido en un HotWaterGenerator y algún tipo de Stirrer . Luego, se puede construir una nueva CoffeeFactory y SoupFactory partir de esos componentes y se puede evitar cualquier mezcla accidental.

Entre los casos más sutiles, la tensión entre objetos de acceso a datos (DAO) y objetos de transferencia de datos (DTO) es muy común. Los DAO hablan con la base de datos, los DTO son serializables para la transferencia entre procesos y máquinas. Por lo general, los DAO necesitan una referencia a su infraestructura de base de datos, por lo tanto, son inutilizables en sus clientes enriquecidos que ni tienen instalados los controladores de base de datos ni tienen los privilegios necesarios para acceder al DB.

Código huele

  • Los métodos en una clase comienzan a agruparse por áreas de funcionalidad ("estos son los métodos de Coffee y estos son los métodos de Soup ").

  • Implementando muchas interfaces.


Aquí hay algunas cosas que me ayudan a descubrir si mi clase está violando a SRP:

  • Complete los comentarios del documento XML en una clase. Si usa palabras como if, and, but, except, when, etc., sus clases probablemente estén haciendo demasiado.
  • Si su clase es un servicio de dominio, debe tener un verbo en el nombre. Muchas veces tiene clases como "OrderService", que probablemente debería dividirse en "GetOrderService", "SaveOrderService", "SubmitOrderService", etc.

Otra regla de oro que me gustaría incluir es la siguiente:

Si siente la necesidad de escribir algún tipo de producto cartesiano de casos en sus casos de prueba, o si desea burlarse de ciertos métodos privados de la clase, se infringe la Responsabilidad individual.

Hace poco tuve esto de la siguiente manera: tenía un árbol de sintaxis abstracta cetain de una corutina que se generará en C más tarde. Por ahora, piensa en los nodos como Sequence, Iteration y Action. La secuencia encadena dos corotines, iteración repite una corrutina hasta que una condición definida por el usuario sea verdadera y Acción realice una cierta acción definida por el usuario. Además, es posible anotar Acciones e Iteraciones con bloques de código, que definen las acciones y condiciones para evaluar a medida que la coroneta avanza.

Era necesario aplicar una cierta transformación a todos estos bloques de código (para los interesados: necesitaba reemplazar las variables de usuario conceptuales por variables de implementación reales para evitar conflictos variables. Aquellos que conocen las macros de lisp pueden pensar en gensym en acción: )). Por lo tanto, lo más simple que funcionaría es un visitante que conoce la operación internamente y solo los llama al bloque de código anotado de la Acción e iteración en visita y atraviesa todos los nodos del árbol de sintaxis. Sin embargo, en este caso, habría tenido que duplicar la afirmación "se aplica la transformación" en mi código de prueba para el método de acción de visita y el método de visita-visita. En otras palabras, tuve que verificar los casos de prueba del producto de las responsabilidades Traversión (== {iteración transversal, acción transversal, secuencia transversal}) x Transformación (bien, código bloque transformado, que explotó en iteración transformada y acción transformada). Por lo tanto, tuve la tentación de utilizar powermock para eliminar el método de transformación y reemplazarlo con algún ''retorno''. Me transformaron! "; ''- Stub.

Sin embargo, de acuerdo con la regla general, dividí la clase en un TreeModifier de clase que contiene una instancia de NodeModifier, que proporciona los métodos modifyIteration, modifySequence, modifyCodeblock, etc. Por lo tanto, podría probar fácilmente la responsabilidad de atravesar, llamar al NodeModifier y reconstruir el árbol y probar la modificación real de los bloques de código por separado, eliminando así la necesidad de las pruebas del producto, porque las responsabilidades estaban separadas ahora (en atravesar y reconstruir y la modificación concreta).

También es interesante notar que más adelante, pude reutilizar en gran medida el TreeModifier en varias otras transformaciones. :)


Si termina con MethodA que usa MemberA y MethodB que usa MemberB y no es parte de algún esquema de simultaneidad o control de versiones, puede estar violando SRP.

Si notas que tienes una clase que solo delega llamadas a muchas otras clases, es posible que estés atrapado en el infierno de la clase proxy. Esto es especialmente cierto si termina instanciando la clase de proxy en todas partes cuando podría usar las clases específicas directamente. He visto mucho de esto. Piense en las clases ProgramNameBL y ProgramNameDAL como sustituto del uso de un patrón Repository.


Los Principios, Patrones y Prácticas Ágiles de Martin en C # me ayudaron mucho a comprender SRP. Él define SRP como:

Una clase debe tener solo una razón para cambiar.

Entonces, ¿qué está impulsando el cambio?

La respuesta de Martin es:

[...] cada responsabilidad es un eje de cambio. (p 116 )

y además:

En el contexto del SRP, definimos una responsabilidad como una razón para el cambio. Si puede pensar en más de un motivo para cambiar una clase, esa clase tiene más de una responsabilidad (página 117 )

De hecho, SRP encapsula el cambio. Si ocurre un cambio, debería tener un impacto local.

¿Donde está YAGNI?

YAGNI se puede combinar muy bien con SRP: cuando aplica YAGNI, espera hasta que se produzca algún cambio. Si esto sucede, debería ser capaz de ver claramente las responsabilidades que se infieren de la (s) razón (es) de cambio.

Esto también significa que las responsabilidades pueden evolucionar con cada nuevo requisito y cambio. Pensando más en SRP y YAGNI le proporcionará los medios para pensar en diseños y arquitecturas flexibles.


Bueno, este principio se debe usar con un poco de sal ... para evitar la explosión de clase.

Una sola responsabilidad no se traduce en clases de método único. Significa una sola razón para existir ... un servicio que el objeto proporciona a sus clientes.

Una buena manera de mantenerse en el camino ... Usar el objeto como metáfora de la persona ... Si el objeto fuera una persona, ¿a quién le pediría que haga esto? Asigna esa responsabilidad a la clase correspondiente. Sin embargo, usted no le pediría a la misma persona que administre sus archivos, calcule los sueldos, emita cheques de pago y verifique los registros financieros ... ¿Por qué querría que un solo objeto hiciera todo esto? ( Está bien si una clase asume múltiples responsabilidades siempre que estén relacionadas y sean coherentes ) .

  • Si emplea una tarjeta CRC, es una buena guía sutil. Si tiene problemas para obtener todas las responsabilidades de ese objeto en una tarjeta CRC, probablemente esté haciendo demasiado ... un máximo de 7 sería un buen marcador.
  • Otro olor a código del libro de refactorización serían las clases ENORMES. La cirugía de escopeta sería otra ... hacer un cambio en un área de una clase causa errores en áreas no relacionadas de la misma clase ...
  • Encontrar que está realizando cambios en la misma clase para correcciones de errores no relacionadas una y otra vez es otra indicación de que la clase está haciendo demasiado.

Escriba una descripción breve pero precisa de lo que hace la clase.

Si la descripción contiene la palabra "y", entonces necesita ser dividida.


La señal obvia es cuando su clase termina pareciéndose a una Gran Bola de Barro , que es realmente lo opuesto a SRP (principio de responsabilidad única).

Básicamente, todos los servicios del objeto deben centrarse en llevar a cabo una sola responsabilidad, lo que significa que cada vez que su clase cambie y agregue un servicio que no respeta eso, sabrá que se está "desviando" de la ruta "correcta";)

La causa generalmente se debe a algunas soluciones rápidas añadidas apresuradamente a la clase para reparar algunos defectos. Entonces, la razón por la que está cambiando la clase suele ser el mejor criterio para detectar si está por romper el SRP.


También he estado tratando de entender los principios SÓLIDOS de OOD , específicamente el principio de responsabilidad única, también conocido como SRP (como nota al margen, vale la pena escuchar el podcast de Jeff Atwood, Joel Spolsky y "Uncle Bob" ). La gran pregunta para mí es: ¿qué problemas SOLID intenta tratar?

OOP se trata de modelar. El objetivo principal del modelado es presentar un problema de una manera que nos permita comprenderlo y resolverlo. El modelado nos obliga a enfocarnos en los detalles importantes. Al mismo tiempo, podemos usar la encapsulación para ocultar los detalles "sin importancia" para que solo tengamos que lidiar con ellos cuando sea absolutamente necesario.

Supongo que deberías preguntarte: ¿Qué problema está tratando de resolver tu clase? ¿Ha salido a la superficie la información importante que necesita para resolver este problema? ¿Están los detalles sin importancia escondidos para que solo tenga que pensar en ellos cuando sea absolutamente necesario?

Al pensar en estas cosas, se obtienen programas que son más fáciles de comprender, mantener y ampliar. Creo que esto está en el corazón de OOD y los principios SÓLIDOS, incluido SRP.


los métodos en su clase deberían ser cohesivos ... deberían funcionar juntos y hacer uso de las mismas estructuras de datos internamente. Si encuentra que tiene demasiados métodos que no parecen estar completamente relacionados, o que parecen operar en cosas diferentes, entonces es muy probable que no tenga una buena responsabilidad única.

A menudo es difícil encontrar responsabilidades inicialmente y, a veces, necesitas usar la clase en diferentes contextos y luego refactorizar la clase en dos clases a medida que comienzas a ver las distinciones. A veces te das cuenta de que es porque estás mezclando un concepto abstracto y concreto. Tienden a ser más difíciles de ver y, nuevamente, el uso en diferentes contextos ayudará a aclarar.