Principio de responsabilidad única - ¿Estoy abusando de él?

13

Para referencia, enlace

Tengo un escenario de prueba en el que en un módulo de la aplicación es responsable de crear las entradas del libro mayor. Hay tres tareas básicas que podrían llevarse a cabo -

  • Ver las entradas del libro mayor existentes en formato de tabla.
  • Crear una nueva entrada de libro mayor usando el botón crear.
  • Haga clic en una entrada de libro mayor en la tabla (mencionada en el primer puntero) y vea sus detalles en la página siguiente. Puede anular una entrada de libro mayor en esta página.

(Hay un par de operaciones / validaciones más en cada página, pero por brevedad lo limitaré a estas)

Así que decidí crear tres clases diferentes -

  • LedgerLandingPage
  • CreateNewLedgerEntryPage
  • ViewLedgerEntryPage

Estas clases ofrecen los servicios que podrían llevarse a cabo en esas páginas y las pruebas de Selenium usan estas clases para llevar la aplicación a un estado en el que podría hacer una afirmación.

Cuando lo revisaron con un colega, él se sorprendió y me pidió que hiciera una sola clase para todos. Aunque todavía siento que mi diseño está muy limpio, tengo dudas si uso excesivamente el principio de Responsabilidad Única

    
pregunta Tarun 30.11.2011 - 06:57

8 respuestas

19

Citando @YannisRizos aquí :

  

Es un enfoque instintivo y probablemente sea correcto, ya que es más probable que   El cambio es la lógica de negocios de la gestión de los libros de contabilidad. Si eso sucede, sería mucho más fácil.   para mantener una clase de tres.

No estoy de acuerdo, al menos ahora, después de unos 30 años de experiencia en programación. Las clases más pequeñas IMHO son mejores para mantener en casi todos los casos que se me ocurren en casos como este. Cuantas más funciones coloque en una clase, menos estructura tendrá y la calidad de su código se degrada, cada día un poco. Cuando entendí a Tarun correctamente, cada una de estas 3 operaciones

LedgerLandingPage

CreateNewLedgerEntryPage

ViewLedgerEntryPage

es un caso de uso, cada una de estas clases consta de más de una función para realizar la tarea relacionada, y cada una puede desarrollarse y probarse por separado. Por lo tanto, crear clases para cada uno de ellos agrupa las cosas que pertenecen juntas, lo que hace que sea mucho más fácil identificar la parte del código que se va a cambiar si se quiere cambiar algo.

Si tiene funcionalidades comunes entre esas 3 clases, pertenecen a una clase base común, a la clase Ledger en sí (supongo que tiene una, al menos, para las operaciones CRUD) o en una clase auxiliar separada.

Si necesita más argumentos para crear muchas clases más pequeñas, recomiendo echar un vistazo al el libro de Robert Martin "Clean Code ".

    
respondido por el Doc Brown 30.11.2011 - 08:24
11

No hay una implementación definitiva del Principio de Responsabilidad Única, o cualquier otro principio. Debes decidir en base a lo que es más probable que cambie.

Cuando @Karpie escribe en una respuesta anterior :

  

Solo necesita una clase, y la única responsabilidad de esa clase es administrar los libros de contabilidad.

Este es un enfoque instintivo y probablemente sea correcto, ya que lo más probable que cambie es la lógica de negocios de la administración de los libros de contabilidad. Si eso sucede, sería mucho más fácil mantener una clase que tres.

Pero eso debe ser examinado y decidido por caso. Realmente no debería preocuparse por las preocupaciones con la posibilidad minúscula de cambio y concentrarse aplicando el principio sobre lo que es más probable que cambie. Si la lógica de administrar ledgers es un proceso de múltiples capas y las capas son propensas a cambiar independientemente o son preocupaciones que deberían estar separadas en principio (lógica y presentación), entonces debería usar clases separadas. Es un juego de probabilidad.

Una pregunta similar sobre el tema del uso excesivo de principios de diseño, que creo que encontraría interesante:

    
respondido por el yannis 30.11.2011 - 07:45
4

Examinemos estas clases:

  • LedgerLandingPage: El rol de esta clase es mostrar una lista de libros de contabilidad. A medida que la aplicación crezca, es probable que desee agregar métodos para clasificar, filtrar, resaltar y fusionar de manera transparente los datos de otras fuentes de datos.

  • ViewLedgerEntryPage: Esta página muestra el libro mayor en detalle. Parece bastante sencillo. Siempre y cuando los datos sean sencillos. Puede anular el libro de contabilidad aquí. También podrías querer corregirlo. O coméntelo, o adjunte un archivo, recibo o número de reserva externo o lo que sea. Y una vez que permitas la edición, definitivamente quieres mostrar un historial.

  • CreateLedgerEntryPage: Si ViewLedgerEntryPage tiene capacidad de edición completa, la responsabilidad de esta clase posiblemente se puede cumplir editando una "entrada en blanco", que puede o no tener sentido.

Los ejemplos de tesis pueden parecer un poco exagerados, pero el punto es que, desde un UX, estamos hablando de características aquí. Una sola característica es definitivamente una responsabilidad única y distinta. Debido a que los requisitos para esa característica pueden cambiar y los requisitos de dos características diferentes pueden cambiarse en dos direcciones opuestas, y luego desearía haber mantenido el SRP desde el principio.
Pero a la inversa, siempre puedes poner una fachada en tres clases, si tener una única interfaz es más conveniente por cualquier motivo que puedas imaginar.

Existe una cosa como SRP overuse : si necesita una docena de clases para leer el contenido de un archivo, es bastante seguro asumir que está haciendo eso.

Si observa el SRP desde el otro lado, en realidad dice que una sola clase debe hacerse cargo de una sola responsabilidad. Sin embargo, tenga en cuenta que las responsabilidades solo existen dentro de los niveles de abstracción. Por lo tanto, tiene mucho sentido que una clase de nivel superior de abstracción cumpla con su responsabilidad delegando el trabajo a un componente de nivel inferior, que se logra mejor a través de inversión de dependencia .

    
respondido por el back2dos 30.11.2011 - 21:34
2

¿Esas clases tienen solo una razón para cambiar?

Si no lo sabe (todavía), es posible que haya ingresado en la trampa del diseño especulativo / sobre ingeniería (demasiadas clases, patrones de diseño demasiado complejos aplicados). No has satisfecho YAGNI . En las primeras etapas del ciclo de vida del software (algunos) los requisitos pueden no estar claros. Por lo tanto, la dirección del cambio actualmente no es visible para usted. Si te das cuenta de eso, mantenlo en una o una cantidad mínima de clases. Sin embargo, esto conlleva una responsabilidad: tan pronto como los requisitos y la dirección del cambio sean más claros, debe repensar el diseño actual y realizar una refactorización para satisfacer la razón del cambio.

Si ya sabe que existen tres razones diferentes para el cambio y que se relacionan con esas tres clases, entonces aplicó la dosis correcta de SRP. Nuevamente, esto conlleva cierta responsabilidad: si está equivocado o si los requisitos futuros cambian inesperadamente, debe replantearse el diseño actual y hacer una refactorización para satisfacer la razón del cambio.

El punto entero es:

  • Mantenga un ojo en los controladores para el cambio.
  • Elija un diseño flexible, que pueda ser refactorizado.
  • Esté preparado para refactorizar el código.

Si tiene esta mentalidad, dará forma al código sin temor al diseño especulativo / sobre ingeniería.

Sin embargo, siempre existe el factor tiempo: para muchos cambios no tendrá el tiempo que necesita. Refactorizar cuesta tiempo y esfuerzo. A menudo me encontré con situaciones en las que sabía que tenía que cambiar el diseño, pero debido a limitaciones de tiempo tuve que posponerlo. Esto sucederá y no se puede evitar. Descubrí que es más fácil refactorizar con código de ingeniería que con código de ingeniería. YAGNI me da una buena guía: en caso de duda, mantenga la cantidad de clases y la aplicación de patrones de diseño a un mínimo.

    
respondido por el Theo Lenndorff 01.12.2011 - 10:35
1

Hay tres razones para invocar SRP como una razón para dividir una clase:

  • para que futuros cambios en los requisitos puedan dejar algunas clases sin cambios
  • para que un error se pueda aislar más rápidamente
  • para hacer la clase más pequeña

Hay tres razones para negarse a dividir una clase:

  • para que los futuros cambios en los requisitos no le hagan realizar el mismo cambio en varias clases
  • para que la búsqueda de errores no implique trazar rutas largas de direccionamiento indirecto
  • para resistir las técnicas de ruptura de encapsulación (propiedades, amigos, lo que sea) que exponen información o métodos a todos cuando solo son necesarios para una clase relacionada

Cuando observo su caso e imagino cambios, algunos de ellos causarán cambios en varias clases (por ejemplo, agregar un campo al libro mayor, tendrá que cambiar los tres) pero muchos no, por ejemplo, agregar clasificación al lista de libros de contabilidad o cambio de las reglas de validación al agregar una nueva entrada de libro de contabilidad. La reacción de su colega es probablemente porque es difícil saber dónde buscar un error. Si algo se ve mal en la lista de libros de contabilidad, ¿se debe a que se agregó de manera incorrecta o hay algún error en la lista? Si hay una discrepancia entre el resumen y los detalles, ¿cuál es el error?

También es posible que su colega piense que los cambios en los requisitos que significan que tendrá que cambiar las tres clases son mucho más probables que los cambios en los que podría cambiar solo una. Esa podría ser una conversación muy útil para tener.

    
respondido por el Kate Gregory 12.12.2011 - 19:07
0

Creo que si el libro mayor era una tabla en db, sugiero colocar estas tareas en una sola clase (por ejemplo, DAO). Si el libro mayor tiene más lógica y no era una tabla en db, sugiero que deberíamos crear más clases para realice estas tareas (pueden ser dos o tres clases) y proporcione una clase de fachada para hacer simplemente para el cliente.

    
respondido por el Mark xie 01.12.2011 - 06:15
0

En mi humilde opinión no deberías usar clases en absoluto. No hay abstracciones de datos aquí. Los libros de contabilidad son un tipo de datos concreto. Los métodos para manipularlos y mostrarlos son funciones y procedimientos. Seguramente habrá muchas funciones de uso común para compartir, como el formato de una fecha. Hay poco lugar para que los datos se abstengan y se oculten en una clase en las rutinas de visualización y selección.

Hay algunos casos para ocultar el estado en una clase para la edición del libro mayor.

Ya sea que esté abusando o no del SRP, está abusando de algo más fundamental: está abusando de la abstracción. Es un problema típico, engendrado por la noción mítica OO es un paradigma de desarrollo sano.

La programación es 90% concreta: el uso de la extracción de datos debe ser raro y cuidadosamente diseñado. La abstracción funcional, por otro lado, debería ser más común, ya que los detalles del lenguaje y la selección de algoritmos deben separarse de la semántica de la operación.

    
respondido por el Yttrill 12.12.2011 - 15:21
-1

Solo necesita una clase, y la única responsabilidad de esa clase es administrar los libros de contabilidad .

    
respondido por el sevenseacat 30.11.2011 - 07:36

Lea otras preguntas en las etiquetas