在MVP中如何在演示者和数据服务之间传输数据

本文关键字:数据 服务 传输 之间 MVP | 更新日期: 2023-09-27 18:02:37

我是MVP模式的初学者,只是想知道关于以下情况的最佳实践。

为了更好地理解,我将通过一个例子来问这个问题。假设我们有一个形式EmployeeView, EmployeePresenter, EmployeeModel和一个封装GetEmployeeByID()方法的DataService类。对于这个演示,我使用具体类。

让我们假设现在在一个win forms应用程序中,我们想要通过ID搜索员工,所以我们在视图中输入ID并按search按钮。在这种情况下,Presenter可能会使用反射来更新EmployeeModel。(此时只有' EmployeeModel.ID '属性有数据)。然后Presenter将与DataService对话。这可以通过两种方式完成

  1. 这里我们将Model传递给DataService,然后它将更新相同的模型并返回到Presenter

类EmployeePresenter{

private void SearchEmployee (Object sender, EventArgs e)
{
    SearchEmployee();
}
private void SearchEmployee()
{
    var EmployeeModel = DataService.GetEmployeeByID(EmployeeMode);
    base.SetViewPropertiesFromModel(EmployeeModel);
}

}

类DataService{

public EmployeeModel GetEmployeeByID(EmployeeModel employee)
{
    //Database code here
    employee.Name= (string) dataReader["name"];
    . 
    .
    .
    return employee;
}

}

  1. 这里我们只传递Model的属性值,然后DataService将创建Model并返回Presenter

类EmployeePresenter{

private void SearchEmployee (Object sender, EventArgs e)
{
    SearchEmployee();
}
private void SearchEmployee()
{
    var EmployeeModel = DataService.GetEmployeeByID(EmployeeMode.ID);
    Base.SetViewPropertiesFromModel(EmployeeModel);
}

}

类DataService{

public EmployeeModel GetEmployeeByID (string employeeID)
{
    //Database code here
    return new BankAccount
    {
     EmployeeName = (string) dataReader["name"],
     .
     .
     . };
}

}

  1. 以上哪一个是可以接受的?
  2. 例如,如果两个实体(例如,雇员和工资实体)的详细信息显示在一个视图上,我们是将这些实体视为两个模型,还是像EmployeeSalary那样视为单个模型?如果是两个模型,我们需要两个主持人吗?
  3. DataService应该总是返回一个商业模式吗?DataService不能返回stingsDataSets for ex?
  4. 演示者可以在视图上设置_view.EmployeeName=EmployeeModel.Name;这样的值吗?

在MVP中如何在演示者和数据服务之间传输数据

  1. 第二个选项会更好,因为你的方法被称为GetEmployeeByID,期望Id的参数而不是整个模型更合乎逻辑。

  2. 与ASP。. NET MVC在这里你不需要你的模型是一个单一的类传递给视图,所以你可以保持两个模型更好的语义结构。

  3. 这取决于你想要达到的目标。同样,如果您的方法被称为GetEmployeeByID,那么预计它将返回类型为Employee的业务模型。你的服务可以返回"字符串或数据集",但这意味着你需要在Presenter中添加映射来将数据集映射到你的Model。

  4. 是的,演示者可以设置这样的值_view.EmployeeName=EmployeeModel.Name,你需要实现_view.EmployeeNameset访问修饰符来渲染文本在一些控件中,如

    public EmployeeName
    {
        set
        {
            // Label Control
            this.lblEmployeeName.Text = value;
        }
    }
    

在某些情况下,通过演示者设置值只会使它变得更复杂,没有任何特别的好处。在这些情况下,你可以使用监督控制器,它是MVP的一种子类型,其中一些与业务逻辑无关的基本呈现留在视图中,更复杂的逻辑在呈现者/控制器中完成。你可以在这里找到相关信息:

http://martinfowler.com/eaaDev/SupervisingPresenter.html

还有另一个子类型叫做被动视图,其中视图只包含控件,呈现者负责将值传递给视图。你可以在这里阅读:

http://martinfowler.com/eaaDev/PassiveScreen.html

EDIT:你也可以看看这个答案来简单理解这两个子类型:什么是mvp被动视图和mvp监督控制器

编辑:这是被动视图代码。我们希望能够从一种表单类型(Windows。形式,Gtk。也希望将来能够轻松地从hibernate切换到ado.net或其他东西。

我更喜欢采用固定参数的混合方法和采用搜索参数列表的通用方法。

例如,GetSomethingByID只是获取一个int作为参数,并返回一个模型。

但是当我需要搜索一个地址时(至少涉及两个表)。一种保存地址数据,如addressno。,搜索名称等。另一个表保存name1, name2等。然后我就会得到一个参数非常多的方法。此时,我没有扩展这两个表中的任何一个。(!)

我们不喜欢超过4个参数的方法。所以我们创建了一个"QueryMethodParameter"对象,我们从视图中使用它。我将举一个例子,对我来说展示比解释我们做什么更容易。

当你在视图中搜索一个地址时执行。

p.Items.Add(new QueryMethodParameterItem("Address", "AddressNumber", addressNumber));
p.Items.Add(new QueryMethodParameterItem("Address", "Searchname", searchName));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Name1", name1));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Name2", name2));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Name3", name3));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Street", street));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Zipcode", zipcode));
p.Items.Add(new QueryMethodParameterItem("MailingAddress", "Location", location));
((AddressSearchPresenter)this.Presenter).DoAddressSearch(p);

AddressSearchPresenter

public void DoAddressSearch(QueryMethodParameter p)
{
  IAddressService addrService = (IAddressService)ApplicationController.GetInstance().ServiceFactory.GetService(typeof(Model.Address), this.ServiceContext);
  IListEx<Model.Address> erg = addrService.LoadAddresses(p);
  this.SetModel(erg);
  _viewItems = new AddressSearchViewItems(erg);
  this.ModelToView();
}

AddressService

public IListEx<Model.Address> LoadAddresses(QueryMethodParameter p)
{
  ICriteria ca = this.ServiceFactory.CreateCriteria(this.Context, typeof(Model.Address));
  ICriteria ma = null;
  for (int i = 0; i < p.Items.Count; i++)
  {
    object val = p.Items[i].Value;
    if (val == null)
    {
      throw new NullReferenceException();
    }
    if (val.GetType() == typeof(string))
    {
      if (!val.ToString().EndsWith("%"))
      {
        val = val.ToString() + "%";
      }
      if (!val.ToString().StartsWith("%"))
      {
        val = "%" + val.ToString();
      }
    }
    if (p.Items[i].ModelName == "Address")
    {
      ca.Add(Expression.Like(p.Items[i].PropertyName, val));
    }
    else if (p.Items[i].ModelName == "MailingAddress")
    {
      if (ma == null)
      {
        ma = ca.CreateCriteria("MailingAddress", "MailingAddress");
      }
      ma.Add(Restrictions.Like(p.Items[i].ModelName + "." + p.Items[i].PropertyName, val));
    }
    else
    {
      throw new NotImplementedException();
    }
  }
  ca.Add(Expression.Gt("AddressID", 0));
  return ca.ListEx<Model.Address>();
}

仍然有一些我们不喜欢的东西。例如,我们必须在服务中硬编码模型名。——>坏我们可能会在将来更改这一点,以生成带有静态字符串或枚举的类,以便在更改字段名时获得编译器错误。

所以第二种方法通常看起来更好。但是你仍然需要解决很多很多的搜索参数。简单ID是最简单的例子。