我对MVP模式有一些问题,特别是在哪里创建我所有类的实例。目前这些都是在program.cs文件中创建的。虽然这有效,但我知道这是糟糕的设计。我很感激,如果有人能给我一些关于如何构建它的指示。
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var browser = new BrowserWindow();
var helper = new MainPresenterHelper();
var userInterface = new MainForm();
var entity = new UserInputEntity();
var readerWriter = new ReaderWriter();
var manager = new IOManager(readerWriter);
var verif = new VerificationManager(manager);
var entityVerification = new EntityVerification(verif);
var logger = new Logger(entity, readerWriter, true);
var verifyRow = new VerifyRow(entity, logger);
var verification = new VerificationOfDataTypes(entity, logger, verifyRow, new Status(), readerWriter);
var verify = new CsvFileVerification(entityVerification, verification, logger);
var cts = new CancellationTokenSource();
var source = new CancellationTokenSourceWrapper(cts);
var presenter = new MainPresenter(userInterface, browser, helper, entity, verify, source);
Application.Run(userInterface);
}
}
答案 0 :(得分:6)
如果你想知道“为什么这篇文章这么长!?”这是因为我正在与汉斯和其他人在C#视频聊天中聊天,并且有一个想法(我认为)他想要学习的内容。
汉斯,
在WinForms中使用MVP是完全可行的,我们已经在旧的Windows CE 6设备(winform风格)应用程序上完成了它,甚至包括完整的CQRS循环。反正。
我要列出的内容对于你的程序来说不是必需的,但我认为这对你作为开发人员非常有用。你基本上需要学习(在我看来)是对象生命周期和依赖层次结构。这些东西可能有正确的名称,但希望它足够描述。
因此,当您的应用程序启动时,您实际执行的操作是实例化所有内容。虽然你的程序可能确实使用了所有这些东西,但是它真的需要将它们全部实例化到这样的一个地方吗?如果不出意外,它让这个班级负责得太多了。当您运行应用程序时,您要做的第一件事就是显示我假设的UI。所以,理想情况下,第一种方法应该存在这一切。
我看到你将你实例化的各种对象传递到下一个对象中,这是一个好的开始 - 这意味着你基本上已经逐行输出了你的依赖树了。但是,在继续执行此操作之前,您需要重新考虑依赖项。基本上,你的目标是这个类需要以便它运行。不要再思考了(即如果这个类需要X,我将不得不得到Y,因为X需要它)。您只想为每个类找到第一层依赖项。
我的建议是放入一个Dependency Container并使用Constructor Injection,这与你现在所做的不同。通常,您可以通过从对象的实际实现中抽象出对象的操作和属性来启动此过程。
即
public interface IDoStuff
{
string AProperty { get; set; }
bool SomeMethod(int anArgument);
}
public class TheImplementation : IDoStuff
{
public string AProperty { get; set; }
public bool SomeMethod(int anArgument)
{
return false;
}
public void AnotherMethod()
{
this.AProperty = string.Empty
}
}
所以,乍一看,你可能想知道这一切是什么意思,这肯定会让你的程序不必要地复杂。好吧,重点是从消费者那里抽象出实现细节。
而不是针对:
public class MyConsumer
{
private readonly TheImplementation myDependency;
public MyConsumer(TheImplementation myDependency)
{
this.myDependency = myDependency
}
public void ExposedMethod()
{
this.myDependency.SomeMethod(14)
}
}
我们的目标是让消费者只参考界面:
public class MyConsumer
{
private readonly IDoStuff myDependency;
public MyConsumer(IDoStuff myDependency)
{
this.myDependency = myDependency
}
public void ExposedMethod()
{
this.myDependency.SomeMethod(14)
}
}
这给你的灵活性!这意味着您可以对实施进行更改,甚至可以完全交换实施,而无需接触消费者。
它也非常适合测试驱动设计,因为您可以将这些接口的实现替换为虚假版本(称为 mocks ),以便您可以测试应用程序的组件(使用者)绝对隔离 - 更快的测试,更好的测试。不再需要测试演示者导致数据库查询,或者意味着您必须启动IIS Express以运行某些类可能需要的WCF服务。
之后,依赖注入变得非常简单。您的应用程序启动变为composition root并处理您的实际实现与接口的绑定。
一旦绑定完成,我曾经使用的所有依赖容器(Ninject是我个人最喜欢的,紧跟Unity)能够自动检测你的依赖树,并通过简单的请求根节点来实例化整个对象图。谈话太多,代码太少=)这是一个例子:
[STAThread]
private static void Main()
{
// assuming ninject
IKernel kernel = new StandardKernel();
// some people hate self binds, but you may find this easier than
// creating interfaces for all your existing classes
kernel.Bind<BrowserWindow>().ToSelf();
kernel.Bind<MainPresenterHelper>().ToSelf();
kernel.Bind<MainForm>().ToSelf();
kernel.Bind<UserInputEntity>().ToSelf();
// this is where we use the splitting implementation from interface
kernel.Bind<IReaderWriter>().To<ReaderWriter>();
kernel.Bind<IIOManager>().To<IOManager>();
kernel.Bind<IVerificationManager>().To<VerificationManager>();
// .... etc
//If you do them all correctly, you can simply have the following line
Application.Run(kernel.Get<MainForm>());
}
我希望这会对你有所帮助吗?如果没有,那该死的,用了很长时间来写这个...... =)