我正在构建一个 WinForms 应用程序,其 UI 仅包含 aNotifyIcon
及其动态填充的ContextMenuStrip
. 有一个MainForm
将应用程序放在一起,但它永远不可见。
我着手尽可能可靠地构建它(使用 Autofac 处理对象图)并且对我的成功感到非常满意,即使在 O 部分也相处得很好。随着我目前正在实施的扩展,我似乎发现了我的设计中的一个缺陷,需要进行一些改造;我想知道我需要走的路,但对于如何准确定义依赖关系有点不清楚。
如上所述,菜单部分是在启动应用程序后动态填充的。为此,我定义了一个IToolStripPopulator
接口:
public interface IToolStripPopulator
{
System.Windows.Forms.ToolStrip PopulateToolStrip(System.Windows.Forms.ToolStrip toolstrip, EventHandler itemclick);
}
它的一个实现被注入到MainForm
,Load()
方法调用PopulateToolStrip()
和ContextMenuStrip
表单中定义的处理程序。填充器的依赖项仅与获取用于菜单项的数据有关。
这种抽象已经通过几个演进步骤很好地工作,但当我需要多个事件处理程序时不再足够了,例如因为我正在创建几个不同的菜单项组 - 仍然隐藏在单个IToolStripPopulator
界面后面,因为表单不应该是完全关心这个。
正如我所说,我想我知道一般结构应该是什么样的 - 我将IToolStripPopulator
接口重命名为更具体的*并创建了一个新接口,其PopulateToolStrip()
方法不带EventHandler
参数,而是注入到对象中(也允许关于实现等所需的处理程序数量的更大灵活性)。这样,我的“最重要”IToolStripPopulator
可以很容易地成为任意数量的特定适配器的适配器。
现在我不清楚的是我应该如何解决 EventHandler 依赖项。我认为处理程序都应该在 中定义MainForm
,因为它具有正确响应菜单事件所需的所有其他依赖项,并且它还“拥有”菜单。这意味着我对IToolStripPopulator
最终注入 MainForm 的对象的依赖项需要MainForm
使用Lazy<T>
.
我的第一个想法是定义一个IClickHandlerSource
接口:
public interface IClickHandlerSource
{
EventHandler GetClickHandler();
}
这是由我实现的MainForm
,我的具体IToolStripPopulator
实现依赖于Lazy<IClickHandlerSource>
. 虽然这可行,但它是不灵活的。我要么必须为可能越来越多的处理程序(严重违反MainForm
该类的 OCP)定义单独的接口,要么必须不断扩展IClickHandlerSource
(主要违反 ISP)。直接依赖事件处理程序在消费者方面看起来是个好主意,但是通过惰性实例(或类似)的属性单独连接构造函数似乎非常混乱 - 如果可能的话。
我目前最好的选择似乎是:
public interface IEventHandlerSource
{
EventHandler Get(EventHandlerType type);
}
该接口仍将由MainForm
惰性单例实现并注入,并且EventHandlerType
将是具有我需要的不同类型的自定义枚举。这仍然不是非常符合 OCP,但相当灵活。除了新的事件处理程序本身和(可能)新编写EventHandlerType
的.MainForm
IToolStripPopulator
或者....它的单独实现IEventHandlerSource
(作为唯一对象)依赖于Lazy<MainForm>
并将EventHandlerType
选项解析为定义的特定处理程序MainForm
?
我正在尝试以MainForm
一种可行的方式真正摆脱事件处理程序,但现在似乎还不太可能。
我最好的选择是什么,提供不同事件处理程序的最松散耦合和最优雅的解决方案?
[*是的,我可能应该单独留下这个名字来真正遵守 OCP,但这样看起来更好。]