architecture - Escribir código controlado por evento mantenible
documentation node.js (7)
Recientemente empecé a jugar con arquitecturas basadas en eventos, provenientes de una mentalidad orientada a objetos bastante estándar.
Lo primero que noté fue que la dificultad para comprender y rastrear a través de los programas parece aumentar exponencialmente con el tamaño del programa. Si bien los proyectos de mascotas pequeñas son fáciles de seguir, parece que el código se convertirá rápidamente en espaguetis.
Entiendo que soy nuevo en esta mentalidad de desarrollo y no todas mis preocupaciones orientadas a objetos se transfieren. ¿Hay recursos para escribir código manejable de eventos comprensible y mantenible? ¿Qué hacen las personas que usan node.js o Twisted o Event Machine sobre esto?
Hice una charla sobre este tema en Yahoo el año pasado.
Mi único consejo es pensar funcional .
Obviamente, ya existen mejores prácticas y modelos que continuarán desarrollándose con el tiempo.
Sin embargo, considere también la posibilidad de que la programación programada ofrezca la oportunidad de que los "proyectos de mascotas pequeñas" interactúen entre sí. Imagine un mundo donde miles de proyectos individuales distribuidos interactúan en tiempo real a través de devoluciones de llamadas definidas por el usuario.
Los usuarios y desarrolladores podrían volver a cablear la web y las aplicaciones sobre los protocolos existentes de arriba a abajo en lugar de confiar en el diseño de la aplicación existente. Los diseñadores de aplicaciones podrían entonces concentrarse en casos de uso individuales en lugar de proporcionar soluciones únicas para todos o preocuparse por cada posible contingencia.
Echa un vistazo a Web Hooks y mira cómo los servicios como Twilio ya están funcionando
Para Twisted, en lugar de usar el antiguo deferredGenerator, recomiendo inlineCallbacks; Te permite escribir completamente el código de estilo de bloqueo y seguir jugando bien con el bucle de eventos.
@defer.inlineCallbacks
def foo(arg):
bar = nonBlockingFunction(foo)
output = yield FunctionThatReturnsADeferredToo(bar)
defer.returnValue(output) #This is how we return a result instead of using return
Trate de mirar estos artículos:
Usaré Python como ejemplo, ya que eso es lo que estoy usando para construir enormes aplicaciones distribuidas ahora mismo.
El Python retorcido permite un estilo muy imperativo usando estilos de Generador de aplazado (inlineecallbacks) o (un poco más feos). Estos métodos le permiten escribir procedimientos que utilizan un código de devolución de llamada impulsado por eventos que es mucho más fácil de leer y comprender. La implementación convierte su función en una secuencia perezosa que produce una secuencia de aplazados.
Específicamente, no tiene que crear un conjunto profundamente anidado de funciones de devolución de llamada / lambdas / cierres y, en cambio, puede devolver el control de una función al bucle de eventos en puntos arbitrarios. Mentalmente puede re-etiquetar esto como coroutines o multitarea cooperativa si lo desea. Se hace el trabajo. Un ejemplo sería (usando el estilo de generador de diferido más feo) como este:
@defer.deferredGenerator
def foo(arg):
bar = nonBlockingFunction(foo)
baz = waitForDeferred(aFunctionThatReturnsADeferredToo(bar))
yield baz #Returns control to the event loop
output = baz.getResult() #This gets the output of aFunctionThat...Too above
yield output #This is how we return a result instead of using return
@defer.deferredGenerator
def aFunctionThatReturnsADeferredToo(put_bar_here):
"""Stuff happens here...."""
...etc...
Hay otra publicación aquí que muestra el método inlineCallbacks, que es más limpio, pero requiere python 2.5 o más reciente (es decir, no está incluido en la serie Centos / RHEL 5, que lamentablemente estoy atascado en mi aplicación). Si puedes usarlo, hazlo.
Como puede ver, esto se parece a las cosas imperativas de la pitón de la vieja escuela que usted conoce y ama, pero es mucho más fácil de mantener sin un montón de funciones anidadas y lambdas. Aunque todavía deseo que la pitón tuviera bloques.
En cuanto a la depuración, puede activar la depuración del reactor torcido utilizando la llamada defer.setDebugging (True) en algún lugar de su código de inicialización. Esto adjuntará el rastreo original que generó una excepción en su código, para que pueda ver de manera trivial dónde ocurrió el error REALMENTE. Solo recuerde redactar la declaración setDebugging antes de comenzar la producción, ya que da como resultado una GRAN cantidad de introspección adicional (mire si es que quiere estar absolutamente horrorizado).
Martyn Loughran escribió un excelente artículo breve sobre cómo evitar los espaguetis de devolución de llamada.
Lo que realmente disfruté de su artículo es el proceso de mejorar los espaguetis en algo agradable y limpio; Puede parecer un poco formalizado al principio, pero cuando vea el resultado final, creo que estará de acuerdo en que muestra un arte real en un código limpio y legible.