Para referencia: http://en.wikipedia.org/wiki/Single_responILITY_principle
Tengo un escenario de prueba en el que en un módulo de aplicación es responsable de crear entradas en el libro mayor. Hay tres tareas básicas que podrían llevarse a cabo:
- Ver las entradas de contabilidad existentes en formato de tabla.
- Cree una nueva entrada de libro mayor con el botón Crear.
- Haga clic en una entrada del 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)
Entonces 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 cierta afirmación.
Cuando lo revisé con mi colega, él estaba abrumado y me pidió que hiciera una sola clase para todos. Aunque todavía siento que mi diseño está muy limpio, dudo si estoy usando en exceso el principio de responsabilidad única
Respuestas:
Citando a @YannisRizos aquí :
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 en mi humilde opinión son mejores para mantener
en casi todos los casos que se me ocurrenen casos como este. Cuantas más funciones ponga en una clase, menos estructura tendrá y la calidad de su código se degrada, cada día un poco. Cuando entendí Tarun correctamente, cada una de estas 3 operacioneses 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, la creación de 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 algo va a cambiar.
Si tiene funcionalidades comunes entre esas 3 clases, pertenecen a una clase base común, la
Ledger
clase 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, le recomiendo que eche un vistazo al libro "Código limpio" de Robert Martin .
fuente
No existe una implementación definitiva del Principio de Responsabilidad Única, ni ningún otro principio. Debe decidir en función de lo que es más probable que cambie.
Como @Karpie escribe en una respuesta anterior :
Ese es un enfoque instintivo y probablemente sea correcto, ya que lo que es más probable que cambie es la lógica comercial de administrar los libros mayores. 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 debe 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 los libros de contabilidad es un proceso de múltiples capas y las capas tienden a cambiar de manera independiente 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 probabilidades.
Una pregunta similar sobre el tema del uso excesivo de principios de diseño, que creo que le resultaría interesante: Patrones de diseño: cuándo usar y cuándo dejar de hacer todo usando patrones
fuente
Examinemos estas clases:
LedgerLandingPage: el papel de esta clase es mostrar una lista de libros mayores. A medida que la aplicación crezca, es probable que desee agregar métodos para ordenar, filtrar, resaltar y fusionar de forma transparente los datos de otras fuentes de datos.
ViewLedgerEntryPage: esta página muestra el libro mayor en detalle. Parece bastante sencillo. Mientras los datos sean directos. Puede anular el libro mayor aquí. También podrías querer corregirlo. O coméntelo, o adjunte un archivo, recibo o número de reserva externa o lo que sea. Y una vez que permite la edición, definitivamente desea mostrar un historial.
CreateLedgerEntryPage: si
ViewLedgerEntryPage
tiene la capacidad de edición completa, la responsabilidad de esta clase puede cumplirse editando una "entrada en blanco", que puede tener sentido o no.Estos ejemplos pueden parecer un poco exagerados, pero el punto es que desde un UX estamos hablando de características aquí. Una característica única es definitivamente una responsabilidad distinta y única. Debido a que los requisitos de esa característica pueden cambiar y los requisitos de dos características diferentes pueden cambiar en dos direcciones opuestas, y luego desearía haberse quedado con el SRP desde el principio.
Pero a la inversa, siempre puede colocar una fachada en tres clases, si tener una sola interfaz es más conveniente por cualquier razón que se le ocurra.
Existe un uso excesivo de SRP : si necesita una docena de clases para leer el contenido de un archivo, es bastante seguro asumir que está haciendo exactamente eso.
Si miras el SRP desde el otro lado, en realidad dice que una sola clase debería ocuparse de una sola responsabilidad. Sin embargo, tenga en cuenta que las responsabilidades existen solo dentro de los niveles de abstracción. Por lo tanto, tiene mucho sentido que una clase de un nivel de abstracción más alto lleve a cabo su responsabilidad delegando el trabajo en un componente de nivel más bajo, que se logra mejor a través de la inversión de dependencia .
fuente
¿Estas clases tienen solo una razón para cambiar?
Si aún no lo sabe, es posible que haya entrado en la trampa de diseño especulativo / sobre ingeniería (demasiadas clases, patrones de diseño demasiado complejos aplicados). No has satisfecho a 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 hay tres razones diferentes para el cambio y que se asignan a esas tres clases, simplemente aplicó la dosis correcta de SRP. Nuevamente, esto conlleva cierta responsabilidad: si está equivocado o si los requisitos futuros cambian inesperadamente, debe repensar el diseño actual y realizar una refactorización para satisfacer la razón del cambio.
El punto completo es:
Si tiene esta mentalidad, dará forma al código sin mucho miedo al diseño especulativo / a la 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 bajo código diseñado que sobre código diseñado. YAGNI me da una buena guía: en caso de duda, mantenga al mínimo la cantidad de clases y la aplicación de patrones de diseño.
fuente
Hay tres razones para invocar SRP como una razón para dividir una clase:
Hay tres razones para negarse a dividir una clase:
Cuando miro su caso e imagino cambios, algunos de ellos causarán cambios en varias clases (por ejemplo, agregar un campo al libro mayor, deberá cambiar los tres), pero muchos no lo harán, por ejemplo, agregar ordenamiento a la lista de libros mayores. o cambiar las reglas de validación al agregar una nueva entrada de libro mayor. 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 mayores, ¿es porque se agregó incorrectamente o hay algo mal en la lista? Si hay una discrepancia entre el resumen y los detalles, ¿cuál está mal?
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 se saldría con el cambio de solo uno. Esa podría ser una conversación realmente útil para tener.
fuente
Creo que si el libro mayor fuera una tabla en db, sugiero que coloque estas tareas de estrés en una clase (por ejemplo, DAO). Si el libro mayor tiene más lógica y no era una tabla en db, sugiero que creemos más clases para hacer estas tareas (puede ser dos o tres clases) y proporcionar una clase de fachada para hacer simplemente para el cliente.
fuente
En mi humilde opinión, no deberías estar usando 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. Sin duda habrá muchas funciones de uso común para compartir, como formatear una fecha. Hay poco lugar para que los datos se abstraigan y se oculten en una clase en las rutinas de visualización y selección.
Hay algún caso para ocultar el estado en una clase para la edición del libro mayor.
Ya sea que estés abusando o no del SRP, estás abusando de algo más fundamental: estás abusando de la abstracción. Es un problema típico, engendrado por la noción mítica OO es un paradigma de desarrollo sensato.
La programación es 90% concreta: el uso de la abstracció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 elección de algoritmos deben separarse de la semántica de la operación.
fuente
Solo necesita una clase, y la responsabilidad única de esa clase es administrar los libros mayores .
fuente