Tea*_*Dev 13 c# dependency-injection inversion-of-control winforms solid-principles
我正在构建一个WinForms应用程序,其UI只包含一个NotifyIcon动态填充的UI ContextMenuStrip.有一个MainForm共同持有的申请,但这是从来没有看见.
我开始尽可能安全地构建它(使用Autofac来处理对象图)并且对我的成功感到非常满意,即使使用O部分也相当不错.随着我目前正在实施的扩展,似乎我发现了我的设计中的一个缺陷,需要重新改造一下; 我想知道我需要去的方式但是有点不清楚如何准确定义依赖关系.
如上所述,菜单部分在启动应用程序后动态填充.为此,我定义了一个IToolStripPopulator接口:
public interface IToolStripPopulator
{
System.Windows.Forms.ToolStrip PopulateToolStrip(System.Windows.Forms.ToolStrip toolstrip, EventHandler itemclick);
}
Run Code Online (Sandbox Code Playgroud)
将此实现注入到MainForm,并且Load()方法PopulateToolStrip()使用ContextMenuStrip表单中定义的处理程序调用.populator的依赖关系仅与获取用于菜单项的数据有关.
这个抽象通过一些进化步骤很好地工作,但是当我需要多个事件处理程序时,这已经不够了,例如因为我正在创建几个不同的菜单项组 - 仍然隐藏在单个IToolStripPopulator界面后面,因为表单不应该是完全关心这一点.
正如我所说,我想我知道一般结构应该是什么样的 - 我将IToolStripPopulator接口重命名为更具体的东西*并创建了一个新PopulateToolStrip()方法,其方法不接受EventHandler参数,而是将其注入到对象中(也允许更多关于实现所需的处理程序数量的更多灵活性等).这样,我的"最重要的" IToolStripPopulator可以很容易地成为任何数量的特定适配器的适配器.
现在我不清楚的是我应该如何解决EventHandler依赖项.我认为处理程序应该全部定义MainForm,因为它具有正确响应菜单事件所需的所有其他依赖项,并且它还"拥有"菜单.这意味着我IToolStripPopulator最终注入到MainForm中的MainForm对象的依赖关系需要依赖于对象本身Lazy<T>.
我的第一个想法是定义一个IClickHandlerSource接口:
public interface IClickHandlerSource
{
EventHandler GetClickHandler();
}
Run Code Online (Sandbox Code Playgroud)
这是由我实现的MainForm,我的具体IToolStripPopulator实现依赖于Lazy<IClickHandlerSource>.虽然这有效,但它是不灵活的.我要么必须为可能越来越多的处理程序定义单独的接口(严重违反OCP与MainForm类)或不断扩展IClickHandlerSource(主要违反ISP).直接依赖事件处理程序在消费者方面看起来是一个好主意,但是通过惰性实例(或类似)的属性单独连接构造函数似乎非常混乱 - 如果可能的话.
我最好的打赌似乎是这样的:
public interface IEventHandlerSource
{
EventHandler Get(EventHandlerType type);
}
Run Code Online (Sandbox Code Playgroud)
该接口仍将由MainForm一个懒惰的单例实现并注入,并且EventHandlerType将是一个具有我需要的不同类型的自定义枚举.这仍然不是非常符合OCP,但相当灵活.除了新的事件处理程序本身和(可能)新编写的附加实现之外,EventHandlerType显然每个新类型的事件处理程序都会有一个更改,就像解析逻辑一样.MainFormIToolStripPopulator
或者....它的一个单独的实现IEventHandlerSource(作为唯一的对象)依赖于Lazy<MainForm>并解析在中EventHandlerType定义的特定处理程序的选项MainForm?
我试图想出一种MainForm以可行的方式实际获取事件处理程序的方法,但现在看起来不太可能.
这里我最好的选择是什么,为不同的事件处理程序提供最松散的耦合和最优雅的分辨率?
[*是的,我可能应该单独留下名字来真正遵守OCP,但它看起来更好.]
如果您在表单中托管子组件,它们可以填充主应用程序“shell”。
您可以有一个ShellComponent继承自 System.Windows.Forms.ContainerControl 的基类。这也将为您提供一个设计界面。您可以拥有这样的 ITool,而不是依赖 IToolStripPopulator。
public inteface ITool<T>
{
int ToolIndex { get; }
string Category { get; }
Action OnClick(T eventArgs);
}
Run Code Online (Sandbox Code Playgroud)
每次加载视图时,您都可以从 MainForm 中调用ShellComponent。public List<ITool> OnAddTools(ToolStrip toolStrip)这样该组件将负责填充工具条。
然后,“ShellComponent”会向 IoC 容器询问实现ITool<T>. 这样,您ITool<T>就提供了一种分离事件(在MainForm或 中ContainerControl)并将其推送到任何类的方法。它还允许您准确定义要传递的参数(与 MouseClickEventArgs 等相反)。
| 归档时间: |
|
| 查看次数: |
2052 次 |
| 最近记录: |