抽象是好的,但在某些时候记住这一点很重要某物必须对一两件事了解一两件事,否则我们只会在地板上放一堆抽象的乐高积木,而不是将它们组装成房子。
控制反转/依赖注入/flippy-dippy-upside-down-whatever-we-call-it-this-week 容器,例如Autofac http://code.google.com/p/autofac/确实可以帮助将这一切拼凑在一起。
当我组装一个 WinForms 应用程序时,通常会得到一个重复的模式。
我将从Program.cs
配置 Autofac 容器的文件,然后获取该容器的实例MainForm
从中,并显示MainForm
。有些人将其称为外壳、工作区或桌面,但无论如何它是具有菜单栏并显示子窗口或子用户控件的“表单”,当它关闭时,应用程序退出。
接下来就是前面提到的MainForm
。我做了一些基本的事情,比如拖放一些SplitContainers
and MenuBar
在 Visual Studio 视觉设计器中进行诸如此类的操作,然后我开始喜欢代码:我会将某些关键接口“注入”到MainForm
的构造函数,以便我可以使用它们,以便我的 MainForm 可以编排子控件,而无需真正了解它们。
例如,我可能有一个IEventBroker
允许各种组件发布或订阅“事件”的接口,例如BarcodeScanned
or ProductSaved
。这允许应用程序的各个部分以松散耦合的方式响应事件,而不必依赖连接传统的 .NET 事件。例如,EditProductPresenter
这与我的EditProductUserControl
可以说this.eventBroker.Fire("ProductSaved", new EventArgs<Product>(blah))
和IEventBroker
将检查该事件的订阅者列表并调用他们的回调。例如,ListProductsPresenter
可以监听该事件并动态更新ListProductsUserControl
它所附着的。最终结果是,如果用户将产品保存在一个用户控件中,则另一个用户控件的演示者可以做出反应并更新自身(如果它恰好是打开的),而任一控件都不必知道彼此的存在,并且无需MainForm
必须精心策划该事件。
如果我正在设计一个 MDI 应用程序,我可能有MainForm
实施一个IWindowWorkspace
接口有Open()
and Close()
方法。我可以将该界面注入到我的各个演示者中,以允许他们打开和关闭其他窗口,而无需他们意识到MainForm
直接地。例如,ListProductsPresenter
可能想开一个EditProductPresenter
以及相应的EditProductUserControl
当用户双击数据网格中的一行时ListProductsUserControl
。它可以参考一个IWindowWorkspace
——这实际上是MainForm
,但它不需要知道这一点——并调用Open(newInstanceOfAnEditControl)
并假设该控件以某种方式显示在应用程序的适当位置。 (这MainForm
据推测,实现会将控件交换到面板上某处的视图中。)
但到底怎么会ListProductsPresenter
create的那个实例EditProductUserControl
? Autofac的代理工厂 http://code.google.com/p/autofac/wiki/DelegateFactories在这里真的很有趣,因为您只需将委托注入到演示者中,Autofac 就会自动将其连接起来,就像它是一个工厂一样(伪代码如下):
public class EditProductUserControl : UserControl
{
public EditProductUserControl(EditProductPresenter presenter)
{
// initialize databindings based on properties of the presenter
}
}
public class EditProductPresenter
{
// Autofac will do some magic when it sees this injected anywhere
public delegate EditProductPresenter Factory(int productId);
public EditProductPresenter(
ISession session, // The NHibernate session reference
IEventBroker eventBroker,
int productId) // An optional product identifier
{
// do stuff....
}
public void Save()
{
// do stuff...
this.eventBroker.Publish("ProductSaved", new EventArgs(this.product));
}
}
public class ListProductsPresenter
{
private IEventBroker eventBroker;
private EditProductsPresenter.Factory factory;
private IWindowWorkspace workspace;
public ListProductsPresenter(
IEventBroker eventBroker,
EditProductsPresenter.Factory factory,
IWindowWorkspace workspace)
{
this.eventBroker = eventBroker;
this.factory = factory;
this.workspace = workspace;
this.eventBroker.Subscribe("ProductSaved", this.WhenProductSaved);
}
public void WhenDataGridRowDoubleClicked(int productId)
{
var editPresenter = this.factory(productId);
var editControl = new EditProductUserControl(editPresenter);
this.workspace.Open(editControl);
}
public void WhenProductSaved(object sender, EventArgs e)
{
// refresh the data grid, etc.
}
}
So the ListProductsPresenter
知道关于Edit
功能集(即编辑演示者和编辑用户控件)——这很好,它们是齐头并进的——但它不需要知道所有依赖关系 of the Edit
功能集,而不是依赖 Autofac 提供的委托来解决它的所有这些依赖关系。
一般来说,我发现“演示者/视图模型/监督控制器”之间存在一对一的对应关系(我们不要太关注差异,因为最终它们都非常相似)和“UserControl
/Form
". The UserControl
在其构造函数中接受呈现器/视图模型/控制器,并根据需要进行数据绑定本身,尽可能地遵循呈现器。有些人隐藏UserControl
来自演示者通过界面,例如IEditProductView
,如果视图不是完全被动的,这可能很有用。我倾向于对所有事情使用数据绑定,因此通信是通过INotifyPropertyChanged
不用打扰。
但是,如果演讲者无耻地与观点联系在一起,你的生活就会变得更加轻松。对象模型中的属性是否与数据绑定不匹配?公开一个新属性就是如此。你永远不会有一个EditProductPresenter
and an EditProductUserControl
使用一种布局,然后想要编写与同一演示者一起使用的新版本的用户控件。您只需编辑它们,它们的所有意图和目的都是一个单元,一个功能,演示者仅存在,因为它很容易进行单元测试,而用户控件则不然。
如果您希望某个功能可替换,则需要将整个功能抽象化。所以你可能有一个INavigationFeature
您的界面MainForm
与. 交谈。你可以有一个TreeBasedNavigationPresenter
实现INavigationFeature
并被消耗TreeBasedUserControl
。你可能有一个CarouselBasedNavigationPresenter
这也实现了INavigationFeature
并被消耗CarouselBasedUserControl
。用户控件和演示者仍然齐头并进,但您的MainForm
不必关心它是否与基于树的视图或基于轮播的视图交互,并且您可以将它们交换出来而无需MainForm
成为更明智的人。
最后,你很容易让自己感到困惑。每个人都是迂腐的,并使用略有不同的术语来表达相似的架构模式之间的微妙(通常是不重要的)差异。以我的拙见,依赖注入对于构建可组合、可扩展的应用程序确实有奇迹,因为耦合被降低了。将功能分离为“呈现器/视图模型/控制器”和“视图/用户控件/表单”对于质量来说确实是奇迹,因为大多数逻辑都被引入到前者中,从而可以轻松进行单元测试;将这两个原则结合起来似乎确实是您正在寻找的,您只是对术语感到困惑。
或者,我可能已经充满了它。祝你好运!