[译]大规模JavaScript应用架构模式(三)

本节目录
  • 外观 – 核心的抽象
  • 中介者 – 应用的核心
  • 把它们结合在一起

外观:核心的抽象

外观作为应用核心的抽象,存在于中介者和模块之间 – 理想情况下,他应该是系统中其他模块能唯一了解的部分。
抽象(动名词)的任务是封装出长期稳定的接口,供模块在任何时候使用。这与Nicholas Zakas最先建议的sandbox controller十分相似。 组件将通过外观与中介者通信,所以这个外观必须可依赖。当我提到“通信”,需要澄清一下,外观是中介者的抽象,外观负责监听来自模块的消息,然后将消息返还给中介者。 除了给模块提供接口,外观还兼任“安全警卫”,决定一个模块可以访问系统的那些部分。组件只可以访问他们自己的方法,不可以访问那些不允许他们访问的接口。例如,一个模块可能会发送一个“dataValidationCompletedWriteToDB”消息。这里,安全检查的任务是确保模块拥有数据库写权限。我们想避免的是,模块意外做了一些他们不应该做的事。 简要回顾一下,中介正保持“pub/sub”管理者的角色,但只接受那些通过外观安全检查的感兴趣的消息。

中介者:应用的核心

中介者扮演系统核心的角色。我们已经简要介绍了它的职责,让我们详细介绍一下。 核心的主要工作是管理模块的生命周期。当核心接收到感兴趣的消息时它需要决定应用应该如何响应这个消息 – 意味着核心需要决定一个模块或一组模块是否应该被启动或关闭。
一旦一个模块被启动,它应该自动执行。检测dom树是否已经加载完成不是核心的责任,而且架构中也有足够的余地让模块自己去做这样的决定。
你可能会想什么样的情况模块需要被停止 – 如果应用检测到一个特定的模块已经失败或者正在发生严重的错误,它应该做出决定,防止那个模块的方法继续运行,从而可以让它重新启动。这样做的目标是降低对用户体验的影响。 另外,核心应该能够在不破坏任何事情的前提下添加或删除模块。一个典型的例子是,一些功能可能无法在页面初始化是加载,但是可以根据用户意图动态加载。回到Gmail的例子,Google可以让聊天窗体保持关闭状态,只有当用户表示需要使用这个模块时才加载它。从性能优化的角度,这是有意义的。 核心的另一个任务是错误管理。模块除了会发送正常的消息,也会发送出错消息,进而核心可以对错误做出相应(例如,关闭模块,重新启动它)。作为松耦合的架构的一部分,在不影响其他模块的请提下,拥有足够的余地去引入新的或更好的处理或显示错误的方法,是十分重要的。通过中介者,使用pub/sub机制可以实现这一目标。

把这些结合在一起

模块包含应用的一些功能。他们通过发布消息,来通知系统是否有事情发生 – 这是他们的主要关心的事情。在随后的FAQ中我会提到,模块可能要依赖dom功用方法,但是理想情况下,他们不应该依赖系统中的其他模块。模块不应该关心:
  • 哪些对象或模块会接受他们发布的消息
  • 那些对象在哪里(服务端还是客户端)
  • 有多少对象订阅了通知
外观抽象了系统的核心,避免模块直接接触核心。它订阅感兴趣的消息(来自模块)然后说:“很好,发生了什么?告诉我细节”。它通过检查发送消息的模块是否拥有必要的许可来处理模块安全问题。 中介者(应用的核心)通过中介者模式扮演一个‘Pub/Sub’管理者的角色。它负责模块管理,在必要的时候启动或停止模块。这是依赖动态加载和确保失败模块在需要时可以重新启动的特定用途。 这个架构的结果是模块不在互相依赖。由于解耦的层次比较高,他们可以很容易的被维护和测试,模块可以在无需额外付出的情况下被投入到一个新页面中使用。他们也可以在系统保持运行的情况下被动态的加载和移除。

Pub/Sub之外:自动事件注册

如Michael Mahemoff之间所提到的,当提及大规模JavaScript应用,利用下语言的动态特征可能很有益处。你可以Micheal 的G+ 页面阅读更多相关内容,但这里,我只把注意力放在一点上 — 事件自动注册(automatic event registration -- AER). AER通过引入一个基于命名约定(name conventions)的自动发报模式,解决了如何把订阅者连接到发布者的问题。例如,如果一个模块发布了一个叫做“messageUpdate”的消息,所名为”messageUpdate”的方法都会被自动调用。 这个模式的建立涉及注册所有可能订阅事件的组件,注册所有可能被订阅的事件,然后对于组件集合中的每一个订阅方法,把事件绑定给它。这是一个很有意思的方法,与本文讨论的架构模式也有关,但是也带来了一些挑战。 例如,当对象动态工作时,可能需要在创建时注册自己。可以查看Michael关于AER的博客,他更深入的讨论了如何解决这些问题。

07 Dec 2011