RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 572931
Accepted
Alex Krass
Alex Krass
Asked:2020-10-03 03:12:04 +0000 UTC2020-10-03 03:12:04 +0000 UTC 2020-10-03 03:12:04 +0000 UTC

如何正确实施MVP模型?

  • 772

我试图弄清楚如何改进 WinForms 体验以及分离逻辑和表示。我遇到了一个带有简短示例的 MVP 模板,并试图弄清楚如何使用它。目前我有以下代码,但我认为我做错了什么。此外,多线程的实现根本不适合这个模型。因此问题是,如何在 WinForms 中正确实施 MVP 设计模式?

查看实现

interface ICustomer
{
    string FirstName { get; set; }
    string LastName { get; set; }
    string SurName { get; set; }
    string Address { get; set; }
    string Code { get; set; }

    event Action Search;
    event Action Cancel;
}

public partial class Customer : Form, ICustomer
{
    public string FirstName
    {
        get { return lblFirstName.Text; }
        set { lblFirstName.Text = value; }
    }

    public string LastName
    {
        get { return lblLastName.Text; }
        set { lblLastName.Text = value; }
    }

    public string SurName
    {
        get { return lblSurname.Text; }
        set { lblSurname.Text = value; }
    }

    public string Address
    {
        get { return lblAddress.Text; }
        set { lblAddress.Text = value; }
    }

    public string Code
    {
        get { return txtCode.Text; }
        set { txtCode.Text = value; }
    }

    public event Action Search;
    public event Action Cancel;

    public Customer()
    {
        InitializeComponent();
    }

    private void btnSearch_Click(object sender, EventArgs e)
    {
        Search();
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        Cancel();
    }
}

演示器实现

class CustomerPresenter 
{
    public ICustomer View;
    public ICustomerModel Model;

    public CustomerPresenter(ICustomer view, ICustomerModel model)
    {
        View = view;
        Model = model;

        View.Search += View_Search;
        View.Cancel += View_Cancel;
    }

    private void View_Search()
    {
        new Task(() => {
            View.FirstName = "Alex Krass";
        }).Start();
    }

    private void View_Cancel()
    {

    }
}

模式与挑战

我的模型还是空的,按照我的理解,应该没有问题,通过Unity IoC调用所有这些耻辱。

class UnityIoC
{
    private static UnityContainer unityContainer;

    public static UnityContainer Instance 
    {
        get
        {
            if (unityContainer == null) CreateContainer();
            return unityContainer;
        }
    }

    private static void CreateContainer()
    {
        unityContainer = new UnityContainer();
        unityContainer.RegisterType<ICustomer, Customer>();
        unityContainer.RegisterType<ICustomerModel, CustomerModel>();
    }
} 

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    ApplicationContext context = new ApplicationContext()
    {
        MainForm = UnityIoC.Instance.Resolve<CustomerPresenter>().View as Form
    };
    context.MainForm.Show();

    Application.Run(context);
}

那么,相应地,当我单击“搜索”按钮时,我无法更新 UI,我真的必须将 TextBox 本身转发给 Presenter 中的 ICustomer 并调用 BeginInvoke 吗?或者我只是误解了 MVP 的实施?

更新:

在不阻塞的情况下更新 UI 的问题似乎已经通过使用计时器解决了,当信息需要在完成时Task和之后TaskScheduler,当它需要在完成后更新时Task。

**Вариант 1**

timer = new Timer() { Interval = 1000 };
timer.Tick += timer_Tick;
timer.Start();

private void timer_Tick(object sender, EventArgs e)
{
    UpdateView();
}

**Вариант 2**
task = new Task(new Action(UpdateModel));
task.ContinueWith(new Action<Task>(UpdateView), TaskScheduler.FromCurrentSynchronizationContext());
task.Start();


private void UpdateView(Task task = null)
{
    view.SomeVal = SomeVal;
    view.SomeValNext = SomeValNext;
}
c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    rdorn
    2020-10-03T06:17:34Z2020-10-03T06:17:34Z

    关于设计模式的解释和实现的问题几乎总是有争议的,所以我立即警告你,下面写的所有内容都是我基于我自己的逻辑、理解和使用 WinForms 的实践的个人设想。您可以在关于 Habré 的文章中阅读解释和实施 MVP 的另一种选择

    类比生物学,让我们从只包含简单控件(按钮、图片、带或不带输入的文本字段)的最简单的单细胞(单窗口)应用程序开始。

    1. 单细胞

    让我们定义模式的组件以及应用程序的哪些元素属于它们。

    • M - 模型 - 一个单独的类封装数据工作。对数据的所有操作都只在其中执行。如果我们在显微镜下考虑模型,那么我们可以考虑多线程或异步处理繁重的计算、访问服务和数据库以及模型中固有的其他层。

    • V - 视图 - 模型数据的可视化表示。这包括我们的所有控件,到目前为止是唯一一个从选定角度实际显示我们模型的窗体。

    • P - Presenter - 我不会发明一个特殊的翻译,我们将专注于一个较长但或多或少准确的定义 - 一个组件,负责从模型接收数据并知道如何以及何时显示它,并且还处理用户输入,为适当的方法拉取模型并处理模型事件。

    在最简单的情况下,表示器将是表单类,在其逻辑中,我们组织数据从模型到控件的传输以呈现数据,以及控制事件的处理以将用户操作传输到模型。

    2. 多细胞

    当出现多个窗口或出现复杂的控件时,以前的应用程序模型仍然存在,但使用它变得不方便。

    让我们添加一个 presenter 类,我们上面描述的单细胞类将作为表示。它的主要任务是将模型的正确部分传递给私人演示者,从中聚合事件,并以正确的顺序和数量将这些事件传递给模型,以及将模型事件路由给私人演示者。对于私人主持人——一般的主持人将充当模特。也可以在其中实现对模型的多线程/异步访问,因为 它只与模型和子演示者有关,与私人演示者负责的 UI 元素无关。

    因此,事实证明,我们的应用程序由许多简单的、相对独立的片段组成,在高级演示者的指导下,确保他们的工作一致。

    3.改变模式

    应用程序增长并变得更加复杂,在某些时候甚至多细胞模型也变得不方便。

    现在是记住 WinForms 支持数据绑定的时候了。在这方面,您可以稍微更改模板。它通常在网上被称为MVPVM。

    这里出现了一个新组件 - VM - 视图模型,并且演示者的角色发生了一些变化。VM 本质上是要显示的模型的一部分。Presenter 的任务将不再包括将数据传输到从属视图,而只是创建到必要 VM 的绑定、将绑定绑定到视图以及处理事件。而且我们原则上可以拒绝一个普通的演示者,因为 创建下一个控件时,您只需将所需的 VM 传输给它,剩下的就由它自己完成。的确,在复杂的情况下,我们仍然需要一个事件聚合器,尤其是当来自不同控件的事件相互关联或相互冲突时。

    该模型允许最灵活地扩展应用程序的功能和缩放。

    4。结论

    尽管我分别考虑了每个模型,但事实上,随着应用程序变得越来越复杂,它们会顺畅地依次出现,并且在实践中,所考虑的模型都不会以其纯粹的形式实际出现,即使是看似简单的应用程序也可能需要复杂的组合解决方案,反之亦然。只需遵循逻辑、常识和原则——“越简单越好”。

    那么,相应地,当我单击“搜索”按钮时,我无法更新 UI,我真的必须将 TextBox 本身转发给 Presenter 中的 ICustomer 并调用 BeginInvoke 吗?或者我只是误解了 MVP 的实施?

    根据我建议的选项,您使用第二个选项,其中唯一缺少的是CustomerPresenter搜索完成的事件,您可以获取数据进行显示,表单可以订阅。或者定义一个方法ICustomer并在表单中实现(或覆盖表单的 Control.Update 方法)并在搜索完成并且可以从主线程访问结果时调用它。CustomerDataUpdateCustomerPresenter

    短算法:

    • 按钮(或其他操作),Customer我们将搜索请求传递给CustomerPresenter.
    • CustomerPresenter将请求传递给模型
    • 该模型通过一个单独的线程激活搜索。主线程的组件从现在开始可以免费进行其他有用的工作。
    • 搜索完成,结果加载到模型中。该模型触发搜索完成的事件。
    • CustomerPresenter接收搜索完成事件,从模型接收搜索结果,并:
      1. 调用窗体的 UpdateData 方法(近似名称)并在参数中传递搜索结果。
      2. 触发一个长时间运行的操作的结束
    • Customer, 获取搜索结果 yCustomerPresenter取决于前一项,并显示它们。

    搜索过程中界面无空闲时间。

    也可以根据现有的场景:

    • 按钮(或其他操作),Customer我们将搜索请求传递给CustomerPresenter.
    • CustomerPresenter在单独的线程中调用模型中的搜索方法。主线程的组件从现在开始可以免费进行其他有用的工作。
    • 在使用 Invoke 完成搜索线程后:
      1. 我们调用窗体的 UpdateData 方法(近似名称)并在参数中传递搜索结果。
      2. 我们激活一个长时间运行的操作结束的事件
    • Customer, 获取搜索结果 yCustomerPresenter取决于前一项,并显示它们。

    也可以把一个长搜索包在一个异步方法里,已经在里面等待搜索线程完成而不挂界面,不过我只是理论上想象这个选项,我并没有亲手做。

    • 3

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5