向上或向下?
我是一个非常有视觉的人。我认为我的应用程序是一个层次结构,其中顶部是根,底部是叶子。
我也理解IoC容器不知道它们包含的对象的职责/功能。相反,包含的对象通过一些抽象的接口知道它们的容器,即“上下文”。
UP:(非IoC方式?) 我的活动应该从我的层次结构的底层发送,并通过责任链模式向上鼓泡给他们的父母,以便所包含的对象不知道他们的容器?例如。我的GUI中的一个按钮调度一个CLICKED事件,该事件由一个监听容器窗口捕获,该窗口通过关闭自身来响应。
DOWN:(IoC方式?) 我的事件是否应该通过容器从我的层次结构的顶部发送,并且到达包含直接订阅容器的侦听器,以便容器不知道它们的内容?例如。容器窗口调度CLOSED事件,由包含的按钮对象直接接收,通过关闭自身进行响应,然后关闭窗口跟随窗口。
'Up'对我来说似乎很自然,但由于IoC有一个容器不知道其包含的对象的行为,我不想回应他们的事件。
我意识到让系统的几乎任何部分听一个事件是可能的,但我想了解IoC参与者之间的基本关系,所以我可以正确地构建它们。我假设人们通常不会分散关于他们程序的事件而不考虑结构关系,依赖性等。
我的问题源于IoC系统中的责任放置 - 包含对象负责对容器进行调用,容器负责为其依赖对象提供服务(这反转了非IoC范例 - 因此同义词“依赖性倒置”)。这似乎是IoC非常重要的基本组成部分 - 责任转移。我考虑调用一个对象的函数或者监听它的事件,这是依赖于另一个对象的例子。当一个对象基于另一个对象定义它自己的行为时,我称之为依赖,因为一个对象知道另一个对象并且知道另一个对象的作用。它已在另一个对象的上下文中定义自己。据我所知,IoC的设置使得包含的对象依赖于容器,因此所包含的对象有责任了解容器的所有内容。知道所包含的对象不应该是容器的责任。因此,对于CONTAINER来说,在CONTAINED注入的对象上听事件似乎就像是错误的责任,因为这意味着它知道它的内容。
在学习依赖“注入”和“反转”之后,这个问题已被重新措辞。之前的问题是here。
答案 0 :(得分:4)
为了简洁起见,我将把容器中包含的对象称为“客户端对象”或仅仅是“客户端”......
事件可以合法地在两个方向上流动,而不会在容器和客户端之间引入不需要的依赖关系。实际上,允许容器从客户端接收事件是最小化这些依赖性的好方法。它们为精确的松耦合提供了一条通道,使得容器能够在仍然与它们通信的同时保持对客户实际所处的内容的了解。 只要有问题的事件由容器定义,而不是由客户定义。
您似乎最关心的是客户端对象会触发容器使用的事件。如果这些事件是由客户端对象定义的,那么这将是一个问题。这显然会从容器中创建对客户端对象的硬依赖;容器必须知道那些客户端定义的事件并且专门为它们编码。这将击败IoC的主要思想。但是如果这些事件是由容器定义的,那么这不是一个问题 - 事实上,它是保持容器与客户端对象松散耦合的最佳方式。客户端可以触发这些事件以供容器使用,但是他们没有定义这些事件。它们可能只触发容器知道如何监听的事件集,如容器所定义的。 (当然,他们可以将其他事件用于其他目的,但容器不会知道或关心。)
例如,考虑一下容器提供了显示和打印“视图”的能力,这些视图在UI中是很少的内容框(这似乎是你在帖子中暗示的那种环境,因为你提到过许多GUI示例 - 但这些概念适用于与GUI无关的任何事情。容器公开了一个名为Print的事件,该事件采用了一些关于打印内容的参数(可能是对IPrintable接口的引用,也许是要打印的原始数据,取决于您尝试使用IoC实现的内容)。当用户按下客户端对象上的某个按钮时,在容器内运行的客户端对象可以触发Print事件。然后容器将接收该事件并代表客户端对象处理打印。或者客户端对象可以触发容器将接收的“CloseMe”事件,并且作为响应,将破坏触发事件的客户端对象(以及其他处理,包括可能询问用户他是否确定等)< / p>
相反,容器可以在客户端对象感兴趣的事情发生时触发事件。同样,这些都只在容器中定义。但是客户端对象可以订阅它们。在上一个示例中,容器可能会公开一个PrintFinished事件,客户端对象可以使用该事件,以便在用户自己的UI中向用户显示“您的文档已完成!”的消息。容器可以触发ApplicationClosing消息,在集装箱完全关闭之前,所有客户端对象都可以使用该消息来拆除他们所持有的任何本机资源。这些只是愚蠢,简单的例子,但希望他们能证明这个想法。
总而言之,我认为事件不仅是合法的,而且对于从容器到客户端以及从客户端到容器的两个方向都非常有用。与“向上”或“向下”无关的关键是谁定义了这些事件。容器定义所有。
答案 1 :(得分:1)
容器不应该了解其组件或它们的行为方式。容器的职责通常是实例化依赖的域 - 而不是通信依赖性。容器应该是一个通用的工厂。
这就是结构图!=通信图的原因。我确信您已经意识到,作为工厂,容器是对象图的根,但是响应或发布事件的容器根本没有意义。
你想要的是,管理活动的人是谁。一个专门的组件,它知道关于事件,并且可以代理一个实例到感兴趣的各方。您需要一个通用事件代理。如果你愿意,可以使用事件路由器。
您可能会发现许多实现。比如Cab的EventBroker,Prism的事件聚合器,或者我最喜欢的EventHub。最后一个是通过Kent, a very cool guy广泛与Cab合作以及与Jeremy Miller和Glenn Block通信的经验的高潮。
如果您使用的是容器,则可能熟悉依赖注入和控制反转。需要发布事件的人注入IEventHub并使用强类型主题类调用Publish。需要订阅事件的人也会注入一个IEventHub并实现一个强类型的ISubscriber接口[它基本上暴露了一个强类型的Receive方法]。就是这样。
现在,事件可以[按照业务要求允许]以任何方式旅行,正如他们应该的那样。