Programación basada en eventos: ¿cuándo vale la pena?

19

Ok, sé que el título de esta pregunta es casi idéntico a ¿ Cuándo debo usar la programación basada en eventos? pero las respuestas de dicha pregunta no me han ayudado a decidir si debo usar los eventos en el caso particular que enfrento.

Estoy desarrollando una pequeña aplicación. Es una aplicación simple y, en su mayor parte, su funcionalidad es CRUD básica.

Ante ciertos eventos (al modificar ciertos datos) la aplicación debe escribir una copia local de dichos datos en un archivo. No estoy seguro de cuál es la mejor manera de implementar esto. Yo puedo:

  • Dispara eventos cuando los datos se modifican y enlaza una respuesta (genera el archivo) a dichos eventos. Alternativamente, implemente el patrón de observador. Eso parece una complejidad innecesaria.
  • Llame al código generador de archivos directamente desde el código que modifica los datos. Mucho más simple, pero parece incorrecto que la dependencia sea de esta manera, es decir, parece incorrecto que la funcionalidad principal de la aplicación (código que modifica los datos) se debe acoplar a ese beneficio adicional (código que genera un archivo de copia de seguridad). Sin embargo, sé que esta aplicación no evolucionará hasta un punto en el que ese acoplamiento plantea un problema.

¿Cuál es el mejor enfoque en este caso?

abl
fuente
2
Yo diría que no implemente nada usted mismo, simplemente use un bus de eventos existente. Esto hará la vida mucho más simple ...
Boris the Spider
La programación dirigida por eventos es inherentemente asíncrona. Los eventos pueden o no suceder, en el orden que pretendía o tal vez en otro orden o en absoluto. Si puede lidiar con esa complejidad adicional, hágalo.
Pieter B
Por lo general, el uso de eventos significa que su código se proporciona como devoluciones de llamada y que se invocan desde otro lugar de formas que no puede predecir. Su descripción suena más así cuando sucede algo específico en su código , debe hacer más de lo que requeriría una implementación ingenua. Simplemente codifique las llamadas adicionales.
Thorbjørn Ravn Andersen
No es una diferencia entre eventos y basada en eventos. Vea, por ejemplo, el episodio 355 del podcast de .NET Rocks, que invita a la reflexión, con Ted Faison, ¡ Ted Faison lleva los eventos al límite! ( URL de descarga directa ) y el libro Programación basada en eventos: llevar los eventos al límite .
Peter Mortensen
La entrevista con Ted Faison comienza a los 13 minutos y 10 segundos.
Peter Mortensen

Respuestas:

31

Siga el principio de KISS: manténgalo simple, estúpido o el principio de YAGNI: no lo va a necesitar.

Puedes escribir el código como:

void updateSpecialData() {
    // do the update.
    backupData();
}

O puedes escribir código como:

void updateSpecialData() {
     // do the update.
     emit SpecialDataUpdated();
}

void SpecialDataUpdatedHandler() {
     backupData();
}

void configureEventHandlers() {
     connect(SpecialDataUpdate, SpecialDataUpdatedHandler);
}

En ausencia de una razón convincente para hacer lo contrario, siga la ruta más simple. Las técnicas como el manejo de eventos son poderosas, pero aumentan la complejidad de su código. Requiere más código para funcionar, y hace que lo que sucede en su código sea más difícil de seguir.

Los eventos son muy críticos en la situación correcta (¡imagínese intentar hacer la programación de la interfaz de usuario sin eventos!) Pero no los use cuando pueda BESAR o YAGNI.

Winston Ewert
fuente
Me gusta especialmente el hecho de que mencionaste que disparar eventos cuando se cambian los datos no es trivial.
NoChance
13

El ejemplo que describe de datos simples, donde la modificación desencadena algún efecto, puede implementarse perfectamente con el patrón de diseño del observador :

  • Esto es más sencillo de implementar y mantener que el código completo controlado por eventos.
  • El acoplamiento entre sujeto y observador puede ser abstracto, lo que facilita la separación de las preocupaciones.
  • Es ideal para una o varias relaciones (los sujetos tienen uno o muchos observadores).

El enfoque basado en eventos vale su inversión para escenarios más complejos, cuando pueden ocurrir muchas interacciones diferentes, en un contexto de muchos a muchos, o si se prevén reacciones en cadena (por ejemplo, un sujeto informa a un observador, que en algunos casos quiere modificar el asignatura u otras asignaturas)

Christophe
fuente
1
Estoy confundido, ¿no es el observador solo una forma de implementar eventos?
svick
1
@svick, no lo creo. En la programación dirigida por eventos , tiene un bucle principal que procesa eventos en una relación de muchos a muchos, con remitentes y observadores desacoplados. Creo que el observador puede contribuir procesando un tipo de evento perticular, pero no puede lograr el espectro completo de EDP solo con un observador. Creo que la confusión proviene del hecho de que en el software controlado por eventos, los observadores a veces se implementan en la parte superior del procesamiento de eventos (generalmente MVC con una GUI)
Christophe
5

Como usted dice, los eventos son una gran herramienta para reducir el acoplamiento entre clases; por lo tanto, si bien puede implicar escribir código adicional en algunos idiomas sin soporte integrado para eventos, reduce la complejidad del panorama general.

Podría decirse que los eventos son una de las herramientas más importantes en OO (según Alan Kay: los objetos se comunican enviando y recibiendo mensajes ). Si usa un lenguaje que tiene soporte incorporado para eventos, o trata las funciones como ciudadanos de primera clase, entonces usarlos es obvio.

Incluso en idiomas sin soporte incorporado, la cantidad de repeticiones para algo como el patrón de Observer es bastante mínima. Es posible que pueda encontrar una biblioteca de eventos genérica decente en algún lugar que pueda usar en todas sus aplicaciones para minimizar la repetitiva. (Un agregador de eventos genérico o un mediador de eventos es útil en casi cualquier tipo de aplicación).

¿Vale la pena en una pequeña aplicación? Definitivamente diría que .

  • Mantener las clases desacopladas entre sí mantiene limpio el gráfico de dependencia de clase.
  • Las clases sin dependencias concretas se pueden probar de forma aislada sin tener en cuenta otras clases en las pruebas.
  • Las clases sin dependencias concretas requieren menos pruebas unitarias para una cobertura completa.

Si está pensando "Oh, pero en realidad es solo una aplicación muy pequeña, realmente no importa tanto" , considere:

  • Las aplicaciones pequeñas a veces terminan combinándose con aplicaciones más grandes más adelante.
  • Es probable que las aplicaciones pequeñas incluyan al menos algunas lógicas o componentes que más tarde deberán reutilizarse en otras aplicaciones.
  • Los requisitos para aplicaciones pequeñas pueden cambiar, lo que provoca la necesidad de refactorizar, lo que es más fácil cuando el código existente se desacopla.
  • Se pueden agregar características adicionales más adelante, lo que provoca la necesidad de extender el código existente, que también es mucho más fácil cuando el código existente ya está desacoplado.
  • El código débilmente acoplado generalmente no toma mucho más tiempo para escribir que el código fuertemente acoplado; pero el código estrechamente acoplado tarda mucho más en refactorizarse y probarse que el código débilmente acoplado.

En general, el tamaño de una aplicación no debe ser un factor decisivo para mantener las clases unidas libremente; Los principios SÓLIDOS no son solo para grandes aplicaciones, sino que se aplican a software y bases de código a cualquier escala.

De hecho, el tiempo ahorrado en la unidad que prueba sus clases sueltamente acopladas de forma aislada debería contrarrestar cualquier tiempo adicional dedicado a desacoplar esas clases.

Ben Cottrell
fuente
2

El patrón de observador puede implementarse de una manera mucho más pequeña de lo que lo describe el artículo de Wikipedia (o el libro GOF), asumiendo que sus lenguajes de programación soportan algo como "devoluciones de llamada" o "delegados". Simplemente pase un método de devolución de llamada a su código CRUD (el método de observación, que podría ser un método genérico de "escribir en el archivo" o uno vacío). En lugar de "disparo de evento" simplemente llame a esa devolución de llamada.

El código resultante será solo un mínimo más complejo que llamar directamente al código generador de archivos, pero sin los inconvenientes del acoplamiento estrecho de componentes no relacionados.

Eso te traerá "lo mejor de ambos mundos", sin sacrificar el desacoplamiento por "YAGNI".

Doc Brown
fuente
Eso funciona la primera vez que Doc, pero cuando el evento necesita desencadenar una segunda cosa, es probable que la clase deba modificarse para hacerlo de esta manera. Si usa un patrón de observador, puede agregar tantos comportamientos nuevos como desee sin abrir la copia de seguridad de la clase original para modificarla.
RubberDuck
2
@RubberDuck: el OP estaba buscando una solución "evitando la complejidad innecesaria": si se necesitan diferentes eventos / comportamientos diferentes, probablemente no consideraría el patrón del observador como demasiado complejo. Así que estoy de acuerdo con lo que dijiste, cuando las cosas se vuelven más complejas, un observador completo le servirá mejor, pero solo entonces.
Doc Brown
Una declaración justa, pero me parece una ventana rota.
RubberDuck
2
@RubberDuck: agregar un observador completo, con mecanismos de editor / suscriptor "por si acaso", cuando realmente no es necesario, me parece una ingeniería excesiva, eso no es mejor.
Doc Brown
1
No estoy en desacuerdo con que puede ser sobre ingeniería. Probablemente me siento como lo hago porque es trivial implementarlo en mi pila de opciones. De todos modos, ¿solo aceptaremos estar en desacuerdo?
RubberDuck