2

我对 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);
    }
}
4

1 回答 1

6

如果您想知道“为什么这篇文章这么长!?” 这是因为我在 C# 视频群聊中与 Hans 和其他人聊天,并且对他想要学习的内容有了一个想法(我认为)


汉斯,

在 WinForms 中使用 MVP是完全可行的,我们在旧的 Windows CE 6 设备(winform 风格)应用程序上充分发挥了它的作用,甚至包括完整的 CQRS 循环。反正。

我将要列出的内容对于您的程序来说不是必需的,但我认为它对您作为开发人员非常有用。您本质上需要学习的(在我看来)是对象生命周期和依赖层次结构。这些东西可能有适当的名称,但希望这足够描述性。

因此,当您的应用程序启动时,您实际上正在做的是实例化所有内容。尽管您的程序可能确实使用了所有这些东西,但真的需要像这样将它们全部实例化在一个地方吗?如果不出意外,它会让这个类承担太多责任。当你运行你的应用程序时,你要做的第一件事就是显示一个我假设的 UI。所以,理想情况下,这就是第一种方法中应该存在的全部内容。

我看到您正在将已实例化的各种对象传递给行中的下一个对象,这是一个好的开始 - 这意味着您基本上已经逐行输入了依赖关系树。但是,您需要重新考虑依赖关系,然后才能继续此操作。基本上,你的目标是这个类需要什么才能运行。不要想得更远(即如果这个类需要 X,我必须得到一个 Y,因为 X 需要它)。您只想找到每个类的第一层依赖关系。

我的建议是放入一个依赖容器并使用构造函数注入,与您现在所做的不同。通常,您会通过从对象的实际实现中抽象出对象的操作和属性来开始此过程。

IE

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),以便您可以绝对隔离地测试应用程序的组件(消费者) - 更快的测试,更好的测试。不再需要测试 Presenters 导致数据库查询或意味着您必须启动 IIS Express 才能运行某些类可能需要的 WCF 服务。

之后,依赖注入变得异常简单。您的应用程序启动成为组合根并处理您的实际实现与接口的绑定。

绑定完成后,我曾经使用过的所有依赖容器(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>());
}

我希望这对您有所帮助?如果不是,该死的,花了很长时间才写出来...... =)

于 2012-10-11T12:00:09.263 回答