c# - IOC : Wiring up dependencies on event handlers

标签 c# winforms dependency-injection inversion-of-control solid-principles

我正在构建一个 WinForms 应用程序,其 UI 仅包含 NotifyIcon 及其动态填充的 ContextMenuStrip 。有一个 MainForm 将应用程序保存在一起,但它永远不可见。

我开始尽可能可靠地构建它(使用 Autofac 处理对象图)并且对我的成功非常满意,即使与 O 部分也相处得很好。通过我目前正在实现的扩展,我似乎发现了我的设计中的一个缺陷,需要稍微改造一下;我想知道我需要走的路,但对于如何准确定义依赖关系有点不清楚。

如上所述,菜单在启动应用程序后部分动态填充。为此,我定义了一个 IToolStripPopulator 接口(interface):

public interface IToolStripPopulator
{
    System.Windows.Forms.ToolStrip PopulateToolStrip(System.Windows.Forms.ToolStrip toolstrip, EventHandler itemclick);
}

这个的实现被注入(inject)到 MainForm 中,Load() 方法调用 PopulateToolStrip()ContextMenuStrip 和一个在表单中定义的处理程序。填充器的依赖关系仅与获取用于菜单项的数据有关。

这种抽象通过几个进化步骤已经很好地工作了,但是当我需要多个事件处理程序时就不再足够了,例如因为我正在创建几组不同的菜单项 - 仍然隐藏在单个 IToolStripPopulator 界面后面,因为表单根本不应该关心它。

正如我所说,我想我知道一般结构应该是什么样的 - 我将 IToolStripPopulator 接口(interface)重命名为更具体的东西*并创建了一个新接口(interface),其 PopulateToolStrip() 方法不采用 EventHandler 参数,而是注入(inject)到对象中(也允许在实现等所需的处理程序数量方面具有更大的灵活性)。这样我的“最重要的” IToolStripPopulator 可以很容易地成为任意数量的特定适配器的适配器。

现在我不清楚的是我应该解决 EventHandler 依赖项的方式。我认为处理程序都应该在 MainForm 中定义,因为它具有正确响应菜单事件所需的所有其他依赖项,并且它还“拥有”菜单。这意味着我对最终注入(inject) MainForm 的 IToolStripPopulator 对象的依赖需要使用 MainForm 依赖于 Lazy<T> 对象本身。

我的第一个想法是定义一个 IClickHandlerSource 接口(interface):
public interface IClickHandlerSource
{
    EventHandler GetClickHandler();
}

这是由我的 MainForm 实现的,我特定的 IToolStripPopulator 实现依赖于 Lazy<IClickHandlerSource> 。虽然这有效,但它是不灵活的。我要么必须为数量可能不断增加的处理程序定义单独的接口(interface)(使用 MainForm 类严重违反 OCP)或不断扩展 IClickHandlerSource(主要违反 ISP)。
直接依赖事件处理程序在消费者方面看起来是个不错的主意,但是通过惰性实例(或类似的)的属性单独连接构造函数似乎非常困惑 - 如果可能的话。

我目前最好的选择似乎是这样的:
public interface IEventHandlerSource
{
    EventHandler Get(EventHandlerType type);
}

该接口(interface)仍将由 MainForm 实现并作为惰性单例注入(inject),而 EventHandlerType 将是具有我需要的不同类型的自定义枚举。这仍然不是非常符合 OCP,但相当灵活。 EventHandlerType 显然会对每种新类型的事件处理程序进行更改,MainForm 中的解析逻辑也是如此,除了新的事件处理程序本身和(可能)新编写的 IToolStripPopulator 的附加实现之外。

或者.... IEventHandlerSource 的单独实现(作为唯一对象)依赖于 Lazy<MainForm> 并将 EventHandlerType 选项解析为 MainForm 中定义的特定处理程序?

我正在想办法以可行的方式从 MainForm 中实际获取事件处理程序,但现在似乎不太可能。

我在这里最好的选择是什么,提供不同事件处理程序的最松散耦合和最优雅的解决方案?

[*是的,为了真正遵守 OCP,我可能应该不理会这个名字,但那样看起来更好。]

最佳答案

What is my best option here, providing the loosest coupling and most elegant resolution of the different event handlers?



不存在通用解决方案,它取决于全局应用程序架构。

如果你想要一个 最松 联轴器,EventAggregator在这种情况下,模式可以为您提供帮助(您的 IEventHandlerSource 与此类似):
  • 图案说明 - http://martinfowler.com/eaaDev/EventAggregator.html
  • Prism 中的实现 - http://msdn.microsoft.com/en-us/library/ff921122.aspx

  • 但是,应该非常谨慎地使用全局事件 - 它们可以 涂抹架构,因为在任何地方都可以订阅事件。

    DI 和 IoC 中的重要事情:较低的依赖性不应该知道较高的依赖性。
    我认为,正如 Leon 之前所说,创建一些像 ITool<T> 这样的界面会更好。并将工具列表存储在 MainForm 中.一些 Action 后MainForm将调用此工具中的某些方法。

    关于c# - IOC : Wiring up dependencies on event handlers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11059298/

    相关文章:

    c# - 多重绑定(bind)时出现 Ninject 错误

    c# - 按 Entity Framework 中的子表排序

    c# - 更新 Entity Framework 中的嵌套对象

    c# - ssis 包的 Dynamics Crm 数据验证

    c# - 如何在 Winform 上平滑地加载具有多个控件的表单?

    c# - 多线程系统.Windows.图形

    c# - 如何将事件附加到动态对象或 COM 对象

    C#:如何在备忘录中添加一行?

    android - Dagger 2 基于上下文的范围

    java - Dagger 2 - 将类注入(inject)到声明的接口(interface)中