質問

I'm a beginner to MVP pattern and just want to know the best practices with regard to the following case.

For better understanding I'll ask the question by an example. Lets say we have a form EmployeeView , EmployeePresenter, EmployeeModel and a DataService class which encapsulates GetEmployeeByID() method. For this demonstration I use concrete classes.

Lets say now in a win forms app we want to search employees by ID, so we enter ID in the view and press Search button. In this event the Presenter will update the EmployeeModel probably using reflection. (at this moment only 'EmployeeModel.ID' property has data). Then the Presenter will talk to DataService. This can be done in two ways

  1. Here we pass the Model to DataService and it will then update the same model and return back to the Presenter.

class EmployeePresenter {

private void SearchEmployee (Object sender, EventArgs e)
{
    SearchEmployee();
}

private void SearchEmployee()
{
    var EmployeeModel = DataService.GetEmployeeByID(EmployeeMode);
    base.SetViewPropertiesFromModel(EmployeeModel);
}

}

class DataService {

public EmployeeModel GetEmployeeByID(EmployeeModel employee)
{
    //Database code here

    employee.Name= (string) dataReader["name"];
    . 
    .
    .
    return employee;

}

}

  1. Here we pass only a property value of the Model and then the DataService will create a Model and return to the Presenter.

class EmployeePresenter {

private void SearchEmployee (Object sender, EventArgs e)
{
    SearchEmployee();
}

private void SearchEmployee()
{
    var EmployeeModel = DataService.GetEmployeeByID(EmployeeMode.ID);
    Base.SetViewPropertiesFromModel(EmployeeModel);
}

}

class DataService {

public EmployeeModel GetEmployeeByID (string employeeID)
{
    //Database code here

    return new BankAccount
    {
     EmployeeName = (string) dataReader["name"],
     .
     .
     . };
}

}

  1. Which one of the above is acceptable?
  2. For example if details of two entities (say, employee and salary entities) are shown on a single view, do we consider these entities as two models or as a single model like EmployeeSalary? If its two models do we need two presenters?
  3. Should DataService always return a business model? cant DataService return stings or DataSets for ex?
  4. Can presenter set values on the view like _view.EmployeeName=EmployeeModel.Name; ?
役に立ちましたか?

解決

  1. The second option would be better as your method is called GetEmployeeByID it is more logical to expect a parameter of Id not a whole model.

  2. Unlike ASP.NET MVC here you do not require your model to be a single class passed to the view so you can keep 2 models for better semantic structure.

  3. This depends on what you are trying to achieve. Again if your method is called GetEmployeeByID it is expected to return a business model of type Employee. Your service can return "strings or data sets" but that would mean you would need additional mapping in the Presenter to map the data set to your Model.

  4. Yes the presenter can set values like this _view.EmployeeName=EmployeeModel.Name you would need to implement the set access modifier of _view.EmployeeName to render text in some control like

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

In some cases though setting values trough the presenter just makes it more complex without any particular benefits. In these cases you can user Supervising Controller which is a sub type of MVP where some basic rendering that is not related to the business logic is left in the View and more complex logic is done in the Presenter/Controller. You can find information about it here:

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

There is also another sub type called Passive View where the view only holds Controls and the Presenter is responsible for passing values to the View. You can read about it here:

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

EDIT: You can also look at this answer for a brief understanding of both sub types: What are MVP-Passive View and MVP-Supervising controller

他のヒント

EDIT: This is passive view code. We want to be able to switch from one Form Type (Windows.Forms, Gtk.Forms, etc.) to another and also want to be able to easily switch from hibernate to ado.net or something else in the future.

I would prefer a mix methods who take fixed parameters and generic ones which taking a list of search parameters.

For example GetSomethingByID just would get one int as a parameter and return a Model.

But when I want so search an address (at least two tables are involed). One which holds address data like addressno., searchname and so on. And another table which holds name1, name2, etc. Then I would get a method with an horrible amount of parameters. And at this point I'm not extending any of the two tables.(!)

We don't like methods with more than 4 parameters. So we created a "QueryMethodParameter" object which we use from our views. I will give an example, it's easier for me to show than to explain what we do.

This is executred when you search an address in our view.

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>();
}

There're still some things we don't like. For example we've to hardcode Modelnames in our Service. --> Bad We'll possible change this in the future to generated classes with static strings or enums, to get compiler errors when a field name is changed.

So the second approach in general looks better. But you still need a solution for many, many search parameters. A simple ID is the simplest example.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top