【问题标题】:Events in an Inversion of Control (Dependency Inversion) system go which way?控制反转(依赖反转)系统中的事件会走哪条路?
【发布时间】:2009-10-31 02:05:59
【问题描述】:

向上还是向下

我是一个非常有眼光的人。我将我的应用程序视为一个层次结构,其中顶部是根,底部是叶子。

我还了解到 IoC 容器不知道其包含的对象的职责/功能。相反,包含的对象通过一些抽象接口知道它们的容器,即“上下文”。

UP:(非 IoC 方式?) 我的事件是否应该从我的层次结构的底部分派并通过责任链模式向上冒泡到它们的父级,以便包含的对象不知道它们的容器?例如。我的 GUI 中的一个按钮调度了一个 CLICKED 事件,该事件被一个侦听容器捕获,该窗口通过关闭自身来响应。

DOWN:(国际奥委会方式?) 我的事件是否应该由容器从我的层次结构的顶部分派并到达已直接订阅容器的包含的侦听器,以便容器不知道它们的内容?例如。容器窗口调度一个 CLOSED 事件,该事件由包含的按钮对象直接接收,这些按钮对象通过关闭自身来响应,然后窗口也随之关闭自身。

“Up”对我来说似乎很自然,但由于 IoC 有一个容器不知道其包含的对象的行为,所以我不想响应它们的事件。

我意识到几乎可以让系统的任何部分监听事件,但我想了解 IoC 参与者之间的基本关系,这样我才能正确地构建它们。我假设人们通常不会只是散布关于他们的程序的事件而不考虑结构关系、依赖关系等。

我的问题来自 IoC 系统中的职责放置——调用容器是包含对象的责任,而容器有责任为其依赖对象提供服务(这颠倒了非 IoC 范式——因此同义词“依赖倒置”)。这似乎是 IoC 的一个非常重要的基本组成部分——职责的转移。我认为调用一个对象的函数或监听它的事件都是依赖另一个对象的例子。当一个对象基于另一个对象定义自己的行为时,我称之为依赖关系,因为一个对象知道另一个对象,也知道另一个对象做什么。它已经在另一个对象的上下文中定义了自己。据我了解,IoC 的设置是为了使包含的对象依赖于容器,因此包含的对象应该有责任了解容器的所有信息。容器不应该负责了解包含的对象。因此,让 CONTAINER 监听 CONTAINED 注入对象上的事件在我看来就像是职责错位,因为这意味着它知道一些关于它的内容。

在学习了 Dependency 'Injection' 和 'Inversion' 不同之后,这个问题被重新表述了。找到上一个问题here

【问题讨论】:

    标签: oop events inversion-of-control dependencies


    【解决方案1】:

    为简洁起见,我将容器中包含的对象称为“客户端对象”或仅称为“客户端”...

    事件可以合法地双向流动,而不会在容器和客户端之间引入不必要的依赖关系。事实上,允许容器从客户端接收事件是最小化这些依赖关系的好方法。它们精确地为松散耦合提供了一条路径,以便容器能够在与客户端通信的同时保持对客户端实际情况的无知。 只要有问题的事件是由容器定义的,而不是由客户端定义的。

    您似乎最关心的是客户端对象会触发由容器消耗的事件。如果这些事件是由客户端对象定义的,这将是一个问题。这显然会在容器中创建对客户端对象的硬依赖;容器必须知道那些客户端定义的事件并专门为它们编码。这将破坏 IoC 的主要思想。但如果这些事件是由容器定义的,那么这不是问题——事实上,这是保持容器与客户端对象松散耦合的最佳方式。客户端可能会触发这些事件以供容器使用,但他们没有定义这些事件。它们可能只触发容器知道如何侦听的一组事件,由容器定义。 (当然他们可以为其他目的触发其他事件,但容器不会知道或关心)。

    例如,考虑一下容器提供了显示和打印“视图”的能力,它们是 UI 中的小内容框(这似乎是您在帖子中暗示的那种环境,因为您提到过许多 GUI 示例 - 但这些概念适用于与 GUI 无关的任何事情)。容器公开了一个名为 Print 的事件,该事件接受一些关于打印内容的参数(可能是对 IPrintable 接口的引用,也可能是要打印的原始数据,这取决于您尝试使用 IoC 实现的目标)。当用户按下客户端对象上的按钮之一时,在容器内运行的客户端对象可以触发 Print 事件。然后容器将接收该事件并代表客户端对象处理打印。或者客户端对象可以触发一个“CloseMe”事件,容器将接收该事件,并作为响应,将销毁触发该事件的客户端对象(以及其他处理,包括可能询问用户是否确定等)

    相反,当客户端对象感兴趣的事情发生时,容器可以触发事件。同样,这些都只在容器中定义。但是客户端对象可以订阅它们。在前面的示例中,容器可能会公开一个 PrintFinished 事件,客户端对象可以使用该事件来在他们自己的 UI 中向用户显示一条消息,即“您的文档已完成!”容器可以触发 ApplicationClosing 消息,所有客户端对象都可以使用该消息在容器完全关闭之前拆除它们持有的任何本机资源。这些只是愚蠢、简单的示例,但希望它们能证明这个想法。

    总而言之,我认为事件双向流动不仅合法而且非常有用——从容器到客户端以及从客户端到容器。与“向上”或“向下”无关的关键是谁定义了这些事件。容器定义了它们中的所有

    【讨论】:

    • 这确实是一个高质量的答案。我很欣赏它的伟大。谢谢:)
    【解决方案2】:

    容器不应该知道它们的组件或它们的行为方式。容器的职责通常是实例化依赖的领域——而不是通信依赖。容器应该是通用工厂。

    这就是为什么结构图!=通信图的原因。我相信您已经意识到,作为工厂,容器是您的对象图的根,但是响应或发布事件的容器根本没有意义。

    那么你想要的是一个负责管理事件的人。一个了解关于事件的专用组件,并且可以代理相关方的一个实例。您需要一个通用的事件代理。一个事件路由器,如果你愿意的话。

    您可能会找到一个的许多实现。比如 Cab 的 EventBroker、Prism 的事件聚合器,或者我最喜欢的 EventHub。最后一个是与 Cab 广泛合作以及与 Jeremy Miller 和 Glenn Block 的通信经验的结晶,作者 Kent, a very cool guy

    如果您使用的是容器,您可能熟悉依赖注入和控制反转。需要发布事件的人被注入一个 IEventHub 并使用强类型主题类调用 Publish。需要订阅事件的人也被注入了一个 IEventHub 并实现了一个强类型的 ISubscriber 接口[它基本上公开了一个强类型的 Receive 方法]。就是这样。

    现在,事件可以按照应有的方式 [在业务要求允许的情况下] 以任何方式传播。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-05
      • 2011-04-24
      • 1970-01-01
      • 2011-03-14
      • 1970-01-01
      • 2010-10-31
      • 1970-01-01
      相关资源
      最近更新 更多