Reputation: 4312
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
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;
}
}
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"],
.
.
. };
}
}
EmployeeSalary
? If its two models do we need two presenters?DataService
always return a business model? cant DataService
return stings
or DataSets
for ex?_view.EmployeeName=EmployeeModel.Name;
?Upvotes: 0
Views: 1998
Reputation: 936
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.
Upvotes: 0
Reputation: 2037
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.
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.
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.
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
Upvotes: 2