我最近刚开始使用事件驱动架构,来自非常标准的面向对象的思维方式。
我注意到的第一件事是,理解和跟踪程序的难度似乎随着程序的大小呈指数增长。虽然小型宠物项目很容易遵循,但感觉代码会很快变成意大利面条。
我知道我是这种开发思维的新手,并不是我所有的面向对象的担忧都会继续存在。是否有任何关于编写可维护、可理解的事件驱动代码的资源?使用 node.js 或 Twisted 或 Event Machine 的人对此做了什么?
我最近刚开始使用事件驱动架构,来自非常标准的面向对象的思维方式。
我注意到的第一件事是,理解和跟踪程序的难度似乎随着程序的大小呈指数增长。虽然小型宠物项目很容易遵循,但感觉代码会很快变成意大利面条。
我知道我是这种开发思维的新手,并不是我所有的面向对象的担忧都会继续存在。是否有任何关于编写可维护、可理解的事件驱动代码的资源?使用 node.js 或 Twisted 或 Event Machine 的人对此做了什么?
去年我在雅虎就这个话题做了一次演讲。
试着看看这些文章:
Martyn Loughran 写了一篇关于避免回调意大利面的优秀短文。
我真正喜欢他的文章的地方是把意大利面条改进成漂亮干净的东西的过程。一开始它可能看起来有点正式,但当你看到最终结果时,我想你会同意他在干净、易读的代码中表现出真正的艺术性。
我将使用 Python 作为示例,因为我现在正在使用它来构建大型分布式应用程序。
Twisted python 允许使用 inlinecallbacks 或(稍微丑陋的)deferredGenerator 样式来实现非常命令式的样式。这些方法允许您编写使用更易于阅读和理解的事件驱动回调代码的过程。该实现将您的函数转换为产生一系列延迟的惰性序列。
具体来说,您不必构建一组深度嵌套的回调函数/lambdas/闭包,而是可以在任意点将函数的控制权交还给事件循环。如果您愿意,您可以在心理上将其重新标记为协同程序或协作多任务处理。它完成了工作。一个例子是(使用更丑的 deferredGenerator 样式),如下所示:
@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...
这里有另一个帖子显示了 inlineCallbacks 方法,它更简洁,但需要 python 2.5 或更高版本(意味着不在 Centos/RHEL 5 系列下,我很遗憾地坚持使用我的应用程序)。如果您可以使用它,请这样做。
正如你所看到的,这看起来像你熟悉和喜爱的老式 Python 命令式的东西,但是如果没有大量的嵌套函数和 lambda,它更容易维护。我仍然希望 python 有块。
至于调试,您可以在初始化代码中的某处使用 defer.setDebugging(True) 调用打开扭曲反应器调试。这将附加在代码中引发异常的原始回溯,以便您可以轻松查看错误实际发生的位置。请记住在投入生产之前编辑 setDebugging 语句,因为它会导致大量的额外自省(如果您想完全被吓坏,请在 strace 中观看)。
对于 Twisted,我推荐使用 inlineCallbacks,而不是使用旧的 deferredGenerator;它允许您完全编写阻塞式代码,并且仍然可以很好地使用事件循环。
@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
我唯一的建议是认为功能。