我使用依赖注入(DI)和Ninject作为DI容器创建了一个WinForms MVC应用程序。基本架构如下
Program.cs (WinForms应用程序的主要入口点):
static class Program
{
[STAThread]
static void Main()
{
...
CompositionRoot.Initialize(new DependencyModule());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CompositionRoot.Resolve<ApplicationShellView>());
}
}
DependencyModule.cs
public class DependencyModule : NinjectModule
{
public override void Load()
{
Bind<IApplicationShellView>().To<ApplicationShellView>();
Bind<IDocumentController>().To<SpreadsheetController>();
Bind<ISpreadsheetView>().To<SpreadsheetView>();
}
}
CompositionRoot.cs
public class CompositionRoot
{
private static IKernel ninjectKernel;
public static void Initialize(INinjectModule module)
{
ninjectKernel = new StandardKernel(module);
}
public static T Resolve<T>()
{
return ninjectKernel.Get<T>();
}
public static IEnumerable<T> ResolveAll<T>()
{
return ninjectKernel.GetAll<T>();
}
}
ApplicationShellView.cs (应用程序的主要形式)
public partial class ApplicationShellView : C1RibbonForm, IApplicationShellView
{
private ApplicationShellController controller;
public ApplicationShellView()
{
this.controller = new ApplicationShellController(this);
InitializeComponent();
}
public void InitializeView()
{
dockPanel.Extender.FloatWindowFactory = new CustomFloatWindowFactory();
dockPanel.Theme = vS2012LightTheme;
}
private void ribbonButtonTest_Click(object sender, EventArgs e)
{
controller.OpenNewSpreadsheet();
}
public DockPanel DockPanel
{
get { return dockPanel; }
}
}
其中
public interface IApplicationShellView
{
void InitializeView();
DockPanel DockPanel { get; }
}
ApplicationShellController.cs
public class ApplicationShellController
{
private IApplicationShellView shellView;
public ApplicationShellController(IApplicationShellView view)
{
this.shellView = view;
}
public void OpenNewSpreadsheet(DockState dockState = DockState.Document)
{
SpreadsheetController controller = (SpreadsheetController)GetDocumentController("new.xlsx");
SpreadsheetView view = (SpreadsheetView)controller.New("new.xlsx");
view.Show(shellView.DockPanel, dockState);
}
private IDocumentController GetDocumentController(string path)
{
return CompositionRoot.ResolveAll<IDocumentController>()
.SingleOrDefault(provider => provider.Handles(path));
}
public IApplicationShellView ShellView { get { return shellView; } }
}
SpreadsheetController.cs
public class SpreadsheetController : IDocumentController
{
private ISpreadsheetView view;
public SpreadsheetController(ISpreadsheetView view)
{
this.view = view;
this.view.SetController(this);
}
public bool Handles(string path)
{
string extension = Path.GetExtension(path);
if (!String.IsNullOrEmpty(extension))
{
if (FileTypes.Any(ft => ft.FileExtension.CompareNoCase(extension)))
return true;
}
return false;
}
public void SetViewActive(bool isActive)
{
((SpreadsheetView)view).ShowIcon = isActive;
}
public IDocumentView New(string fileName)
{
// Opens a new file correctly.
}
public IDocumentView Open(string path)
{
// Opens an Excel file correctly.
}
public IEnumerable<DocumentFileType> FileTypes
{
get
{
return new List<DocumentFileType>()
{
new DocumentFileType("CSV", ".csv" ),
new DocumentFileType("Excel", ".xls"),
new DocumentFileType("Excel10", ".xlsx")
};
}
}
}
实现的接口是
public interface IDocumentController
{
bool Handles(string path);
void SetViewActive(bool isActive);
IDocumentView New(string fileName);
IDocumentView Open(string path);
IEnumerable<DocumentFileType> FileTypes { get; }
}
现在与此控制器关联的视图是
public partial class SpreadsheetView : DockContent, ISpreadsheetView
{
private IDocumentController controller;
public SpreadsheetView()
{
InitializeComponent();
}
private void SpreadsheetView_Activated(object sender, EventArgs e)
{
controller.SetViewActive(true);
}
private void SpreadsheetView_Deactivate(object sender, EventArgs e)
{
controller.SetViewActive(false);
}
public void SetController(IDocumentController controller)
{
this.controller = controller;
Log.Trace("SpreadsheetView.SetController(): Controller set successfully");
}
public string DisplayName
{
get { return Text; }
set { Text = value; }
}
public WorkbookView WorkbookView
{
get { return workbookView; }
set { workbookView = value; }
}
...
}
最后,视图接口是
public interface ISpreadsheetView : IDocumentView
{
WorkbookView WorkbookView { get; set; }
}
和
public interface IDocumentView
{
void SetController(IDocumentController controller);
string DisplayName { get; set; }
bool StatusBarVisible { get; set; }
}
现在我的问题。在Seemann的书&#34; .NET中的依赖注入&#34;他谈到了“三呼”模式&#34;这就是我试图在上面实现的内容。代码工作,shell视图显示,并通过MVC模式我的控制器正确打开视图等。但是,我很困惑,因为上面肯定有&#34;服务定位器反模式&#34;的味道。在Seemann小说的第3章中他指出
COMPOSITION ROOT模式描述了应该使用DI容器的位置。然而, 它没有说明如何使用它。 REGISTER RESOLVE RELEASE模式地址 这个问题[...] DI容器应该连续三次使用 称为Register,Resolve和Release的阶段。
以纯粹的形式,REGISTER RESOLVE RELEASE模式表明你应该只是 在每个阶段进行单个方法调用。 Krzysztof Kozimic将此称为三呼模式。
在单个方法调用中配置DI CONTAINER需要更多说明。该 组件的注册应该在单个方法调用中发生的原因是 因为您应该将DI CONTAINER的配置视为单个原子动作。 配置完成后,容器应视为只读。
这听起来像是服务定位器&#34;服务定位器&#34;,为什么这不被视为服务地点?
为了调整我的代码而不是使用Contstructor Injection,我将我的输入代码更改为
[STAThread]
static void Main()
{
var kernel = new StandardKernel();
kernel.Bind(t => t.FromThisAssembly()
.SelectAllClasses()
.BindAllInterfaces());
FileLogHandler fileLogHandler = new FileLogHandler(Utils.GetLogFilePath());
Log.LogHandler = fileLogHandler;
Log.Trace("Program.Main(): Logging initialized");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(kernel.Get<ApplicationShellView>());
}
使用Ninject.Extensions.Conventions
,然后我更改ApplicationShellController
以更正我的代码以通过ctor注入注入IDocumentController
:
public class ApplicationShellController
{
private IApplicationShellView shellView;
private IEnumerable<IDocumentController> controllers;
public ApplicationShellController(IApplicationShellView shellView, IEnumerable<IDocumentController> controllers)
{
this.shellView = shellView;
this.controllers = controllers;
Log.Trace("ApplicationShellController.Ctor(): Shell initialized successfully");
}
...
}
其中
public class SpreadsheetController : IDocumentController
{
private ISpreadsheetView view;
public SpreadsheetController(ISpreadsheetView view)
{
this.view = view;
this.view.SetController(this);
}
...
}
但这会导致循环依赖,我该如何处理?
问题摘要:
CompositionRoot.Resolve<T>()
服务定位器反模式有什么不同?非常感谢你的时间。
答案 0 :(得分:2)
在此过程中的某个时刻,您必须使用服务位置。但是,DI和SL之间的区别在于,在SL中,您将在请求时解析您的服务,而在DI中,您可以在某种工厂(例如控制器工厂)中解析它们,然后构建您的对象并通过参考文献。
您应该创建某种基础结构来调度命令并使用某种工厂来查找创建的对象使用的依赖项。
这样,代码的其余部分没有依赖项解析,除了在构造点之外,您正在遵循DI模式。