国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Model View Presenter (MVP) design pattern close look - Part 1 (Supervising Controller) - VusCode - C

Model View Presenter (MVP) design pattern close look - Part 1 (Supervising Controller)

 

Usually,usage of design patterns is associated with code executing in the lowertiers, but design patterns can be very usefully used in solvingproblems of the UI world too.

Two of my architecture blog posts are covering the examples related to patterns usage in solving the Web problems:

Although young,Model View Presenter pattern has been retired and two variations from original MVP pattern came up from it.

Thislittle series of blog posts would cover in depth architecture andimplementation of the both MVP design pattern successors: Passive View and Supervising Controller. Allof the blog posts would use the same use case and the same Model domainentities, so the downsides and upsides of the different approaches canbe easier notable.  

What is Model View Presenter?

If you are new to Model View Presenter pattern at all, you should check the Model View Presenterentry level post which explains the most important concepts of wiringthe presenter and view in step by step manner. Once you would chewthat, come back to this post and continue reading :)

  As we can see from this diagram:

  • View contains the Presenter instance (view "knows" presenter)
  • Presenter is the only class knowing how to reach to model and retrieve the data needed for performing business logic
  • Presenter talks to the View through the view interface (abstracted representation of the View without UI specific attributes)
  • View doesn‘t know nothing about the Model

Viewresponsibility is to show the data provided by presenter, and purposeof the presenter is to reach to model, retrieve the needed data,performs required processing and returns the UI prepared data to theview.

The biggest difference between the Model View Controller(MVC) and Model View Presenter (MVP) pattern is in the fact that (inMVC) View is aware of model too.

I personally prefer more theMVP pattern because it leads to easier testability and decouplescompletely UI from the data sources, making it by that very testable.

Use case

Today‘suse case would be that we got request to build a simple web page whichwould allow us searching for a desired user and presenting its dataretrieved from DB  on web page. User data which should be resented are:first name, last name and collection of his addresses. Web page issupposed to provide a way of informing the user when there is no userto be retrieved with a given use name.

UI interface shouldoffer the possibility of editing the first name and last name data, butpresenter should persist the data only if there would be some changes.In case UI didn‘t modify data retrieved from DB, presenter shouldn‘tmake DB call at all.

The implementation design should allowtesting the UI layer without using UI specific testing frameworks,preferably with most of the code logic extracted from a page level

Model

Bothpatterns today (passive view and supervising controller) would sharethe same model in their examples. Model for this use case would berepresented with a component called UserRepositry which static classdiagram looks something like this:

The User class is a domain data carrier class which has 4 properties.

Threeof them are required to be presented to web page user:Name, Surname andAddress where the Address is collection of UserAddress objects withCity and Street properties.

Credit card number is userattribute which we don‘t want to present to web page user. Itrepresents the example of domain attribute not required by UI.

The IUserService interface defines methods:

  • GetUser which for a given user name returns user object populated with appropriate retrieved data.
  • SaveUser which persist given user object

UserServiceStubis just a stub class stubbing the operation of user data retrieval froma database. I‘m aware that  in unit tests I could use dynamic mockobjects but for purpose of this blog post treat UserServiceStub as aclass retrieving the data from a DB. The class itself implementsIUserService like this:

   1: public class UserServiceStub:IUserService
   2: {
   3:     #region IUserService Members
   4:  
   5:     public User GetUser(string userName)
   6:     {
   7:         if (userName == "Nikola")
   8:         {
   9:             User newUser = new User();
  10:         
  11:             newUser.Name = "Nikola";
  12:             newUser.Surname = "Malovic";
  13:             newUser.Addresses.Add(new UserAddress("Vaclavske Namesti 11", "Prague, CZE"));
  14:             newUser.Addresses.Add(new UserAddress("5 Clock Tower", "Maynard, MA, USA"));
  15:             return newUser;
  16:         }
  17:         return null;
  18:     }
  19:  
  20:     public void SaveUser(User user)
  21:     {
  22:         // perform saving to DB
  23:     }
  24:  
  25:     #endregion
  26: }

In case that the given user name would be Nikola, the method wouldreturn user object with first name, last name and two addresses. Incase of different user name the method would return null. Save methodis just stubbing the functionality which saves the user data into thedatabase

View markup

Both examples would share the same page html markup code definition which would look something like this:

   1: <div>
   2:     Enter desired user name:<asp:TextBox ID="SearchCriteriaTextBox" runat="server"></asp:TextBox>
   3:     <asp:Button ID="SearchButton" runat="server"  Text="Find user" OnClick="OnUserSearchButtonClicked" />
   4:     <br />
   5:     <asp:Label ID="ResultLabel" runat="server" />
   6:     <br />
   7:     <div id="resultPanel" runat="server" visible="false">
   8:         <asp:Label ID="FirstNameLabel" runat="server" text="First name" />
   9:         <asp:TextBox ID="FirstNameTextBox" runat="server" OnTextChanged="FirstName_TextChanged"  />        
  10:         <br />
  11:         <asp:Label ID="LastNameLabel" runat="server" text="Last name" />
  12:         <asp:TextBox  ID="LastNameTextBox" runat="server" OnTextChanged="LastName_TextChanged"/>        
  13:         <br />
  14:         <asp:Repeater ID="AdressesRepeater" runat="server">
  15:             <HeaderTemplate>
  16:                 <strong>Adresses</strong><br />
  17:             </HeaderTemplate>
  18:             <ItemTemplate>
  19:                 <%# Container.DataItem.ToString() %> <br />
  20:             </ItemTemplate>
  21:         </asp:Repeater>
  22:         <br />
  23:         <asp:Button ID="SaveButton" runat="server" Text="Save changes" OnClick="OnUserSaveButtonClicked" />
  24:     </div>
  25: </div>

On the top of the web form there is a SearchCriteriaTextBox  wherethe user would enter the user name of the user which data he wants tosee. Once he would enter that, he would click on a SearchButton whichclick event would  be handled in OnSearchUser_Click event handler

ResultLabel purpose is to show the informative messages to web page user if there is no user with a given user name etc..

User data successfully retrieved  would be presented inside of the result pannel:

  • first name in FirstNameTextBox,
  • last name in LastNameTestBox
  • user addresses in repeateer called AddressRepeater

Inside of the result panel there is also Save changes button which click instantiates user persisting action

The page in design mode would look something like this:

Supervising controller

Supervising controller is a type of MVP design pattern based on aconcept of data context binding where the view is implementing some"binding" logic on a object received from presenter which containscertain group of business data needed for the whole view. The view istherefore performing some functionality on it‘s own.

The biggest advantage of supervising controller is in the fact thatpresenter is not aware of the view internals, which allows reuse of onepresenter with multiple views using the same context data source butwith different UI elements. Also presenters are in general much simplercomparing with the Passive view presenters which need to take careabout every single aspect of the view.

View interface

Interface contains next abstract representations:

  • label showing error messages is abstracted to StatusMessage - string setter property.
    Labels and in general other "read only" controls should be represented without getter, because user can not modify their value and therefore presenter is not interested in reading their value 
  • Find user button is abstracted to Search event represented with generic EventHandler<SearchEventArgs> delegate signature.
    SearchEventArgs is just an EventArgs using the additional SearchCriteria string property to carry the lookup criteria from the view to presenter on decoupled manner. Something like this:
  • Save button is abstracted to a Save event argument with a default event argument signature
  • Last part of the view interface is a ContextData property. The purpose of this property is to behave as a "view data source".
    The communication between the presenter and the view is been done through that context data object
    Presenter is responsible to set the context data object value with values retrieved from the model and the view is supposed to update itself from that context data object value. View is also responsible to update appropriate context data properties with the values of the UI specific elements. Presenter then detects all the changes and updates the model with them.

View DTO (data transfer object)

Purpose of the DTO (Data transfer object)is to be information mule, without any logic inside which has acollection of properties in which data is carried in encapsulatedmanner. DTO pattern is essential one in implementing SOA systems, butI‘ll skip that aspect for now.

The biggest question related to View DTO is when we should use it atall? The downside of DTO is that we have to map domain object to theDTO which introduces one more level of indirection  ("There is noproblem which can not be solved with one more level abstraction") andadditional coding.

In our little example the IUserService.Get method returns the Userobject so why not pass that object to the view and skip the mappinghustle?
There are couple of cases which could be signals that the DTO mapping is required:

  1. User can be very heavy object which wouldn‘t be serialization optimal if we have distributed system scenarios
  2. In case of Active Record pattern based design it could expose some methods to the UI developer (user.Save()) which could confuse him or tempt him to make view code logic heavy avoiding presenter
  3. We need to present only a subset of the information domain object carries (in this example we don‘t need credit card information on UI level)
  4. We need to translate some of the strongly typed data to more "UI friendly" type of data. (In the example UserAddress type collection data would be translated to a collection of the strings so the repeater could perform easier data binding)

So, in this example UserDTO looking like this:

As we can see the DTO object has first name and last name stringproperties and collection of string addresses. There  is no credit cardnumber property, because it is not required to be presented in UI.
IsDirtygetter property is the property which value would be set to true ifsome of the property values would change. That value would be used fordetermining if the user data should be persisted or not

Data mapping

Data mapperdesign pattern default interpretation includes into the set of datamapper responsibilities beside mapping the data also the data retrievalactivities. Although, I agree  that is the official and correctimplementation, I don‘t like that small functional add on because itbreaks the separation of concernprinciples. IMHO, purpose of the data mapper is just transformation ofthe already loaded data. Mapper should only know "how to map".

So, our little example therefore contains a DataMapper static helper class which class diagram looks like this:

We have overloaded Translate method which performs appropriate data translation for a given method parameter type

Let‘s take a quick look at the implementation of the first one

   1: public static UserDTO Translate(User user)
   2: {
   3:     UserDTO userDTO = new UserDTO();
   4:     userDTO.FirstName = user.Name;
   5:     userDTO.LastName = user.Surname;
   6:     userDTO.Addresses = MapUserAddress(user.Addresses);
   7:     return userDTO;
   8: }
   9:  
  10: private static IEnumerable<string> MapUserAddress(IEnumerable<UserAddress> adresses)
  11: {
  12:     foreach (UserAddress address in adresses)
  13:     {
  14:         yield return string.Format("{0},{1}", address.Street, address.City);
  15:     }
  16: }

This translate method accepts the User type instance as parameter.
Inline 3, method creates the UserDTO instance and then maps the values ofUser instance properties to appropriate properties of UserDTO
Inline 6, the method calls the user address collection mapping methodwhich transform the collection of the UserAddresses into theIEnumerable<string> utilizing the yield c#

View implementation

Presenter - View wire up

The pattern used for establishing wire up between the view and presenter is dependency injection - constructor type.

The  key concepts of this wire up are:

  • presenter has a constructor accepting parameter of view interface type
  • view implements the view interface
  • view in page load constructs a presenter instance and throw himself to the presenter
  • presenter has a pointer then to the view, but the pointer type is view interface so presenter is not knowing nothing about the UI specific aspects of the view he‘s been injected
  • view keeps created instance of the pointer in the page field to support presenter method invocation in case of specific UI events

In code that would look like this:

   1: private UserDetailsPresenter _presenter;
   2:  
   3: protected void Page_Load(object sender, EventArgs e)
   4: {
   5:     // injecting the view into the presenter
   6:     _presenter = new UserDetailsPresenter(this);
   7: }

View interface implementation

The view is a web page implementing the view interface and wiringand mapping  the UI elements properties to the certain view interfacemembers, something like this:

   1: #region IUserDetailsView Members
   2:  
   3: public UserDTO ContextData
   4: {
   5:     get { return Session["Context"] as UserDTO; }
   6:     set
   7:     {
   8:         Session["Context"] = value;
   9:         OnContextDataBound();
  10:     }
  11: }
  12:  
  13: public string StatusMessage
  14: {
  15:     set { ResultLabel.Text = value; }
  16: }
  17:  
  18: public event EventHandler<SearchEventArgs> Search;
  19: public event EventHandler<EventArgs> Save;
  20:  
  21: #endregion

In line 13, we implement the IView.StatusMessage property on a waythat setting that property would end with setting the Web.UI.Label textproperty with the same value.

ContextData property implementation in this example stores ainterface property value to a session variable (to prevent it from postbacks) and calls the OnContextDataBound method where View would updateitself with new context data, on a way similar to this:

   1: private void OnContextDataBound()
   2: {
   3:     FirstNameTextBox.Text = ContextData.FirstName;
   4:     LastNameTextBox.Text = ContextData.LastName;
   5:     AdressesRepeater.DataSource = ContextData.Addresses;
   6:     AdressesRepeater.DataBind();
   7:     resultPanel.Visible = true;
   8: }

So, in lines 3 and 4 we just set the text boxes text property with context object data values.
In line 5 and 6 code performs data binding to the collection of addresses context data
At the end, result panel is been presented to user.

View interface events  are implemented in lines 18 and 19, and they have just trivial event handlers to raise those events, something like this:

   1: protected void OnUserSearchButtonClicked(object sender, EventArgs e)
   2: {
   3:     if (Search != null)
   4:         Search(this, new SearchEventArgs(SearchCriteriaTextBox.Text));
   5: }
   6:  
   7: protected void OnUserSaveButtonClicked(object sender, EventArgs e)
   8: {
   9:     if (Save != null)
  10:         Save(this, new EventArgs());
  11: }

View routing logic is implementing the functionality of reflecting the UI specific changes to the appropriate context data object members.
Implementation could be trivial as the next code :

   1: protected void FirstName_TextChanged(object sender, EventArgs e)
   2: {
   3:     ContextData.FirstName = FirstNameTextBox.Text;
   4: }
   5:  
   6: protected void LastName_TextChanged(object sender, EventArgs e)
   7: {
   8:     ContextData.LastName = LastNameTextBox.Text;
   9: }

At the end of the view code implementation, the view needs to wire up itself with presenter (MVP diagram shows that view contains the instance of presenter) and we can do that by doing something like this:

   1: UserDetailsPresenter _userDetailsPresenter;
   2: protected void Page_Load(object sender, EventArgs e)
   3: {
   4:     // injecting the view into the presenter
   5:     _userDetailsPresenter=new UserDetailsPresenter(this);
   6: }

As we can see in line 5, the view constructs presenter instance andthrows itself to the presenter constructor parameter so the presentercould have a pointer to the  view interface defined members.

Presenter

Class diagram of the presenter looks like this:

As we can see from the diagram, presenter has two publicconstructors and two private methods handling the appropriate viewinterface events. Presenter has also two fields which hold thepresenter pointers to the view and service layers.

Presenter initialization

All the business logic of controlling,presenting and updating modeland interaction with view should be encapsulated in the presenter. Inthis example Presenter is getting pointer to the view interface andmodel services through the utilization of constructor type of dependency injection design pattern.

   1: public UserDetailsPresenter(IUserDetailsView view, IUserService userService)
   2:  {
   3:      _view = view;
   4:      _userService = userService;
   5:      _view.Search += (OnUserSearch);
   6:      _view.Save += new EventHandler<EventArgs>(OnUserSave);
   7:  }
   8:  
   9:  public UserDetailsPresenter(IUserDetailsView view)
  10:      : this(view, new UserServiceStub())
  11:  {
  12:  }

I won‘t detail here too much about how DI works (if you need that, check out the Model View Presenterpost) but summarized whatever the classes implementing the view andservice interfaces would be, its pointers would be stored inside of thepresenter object and the presenter would use them without having aknowledge what are the exact classes implementing the interfaces.

That‘s how the presenter would know only about the view interface (which abstracts the UI concepts) and not the view.

In line 5 and 6 we can see that the presenter is subscribing to the view save and search events.

The purpose of the first constructor is primary to enableappropriate unit testing of the presenter, but it won‘t be used in realworld use cases. The reason why is like that lies in the fact that theview should be ignorant about the model in MVP pattern, so the viewshouldn‘t know which model service class should inject to the presenter.

Second constructor is the one used by a view and we can se that itis enough for a view to pass a reference to itself  to a constructorand the Poor man dependency injectionwould occur in this(view, new UserServiceStub())  so theUserServiceStub instance pointer would be by default injected into thepresenter.

One additional benefit of having those two constructors could bethat the first one could be used (together with mocking) inimplementing unit tests and the second one could be called inintegration tests (where we need tests to talk with real, non mockedmodel representations like database for e.g.)

Presenter implementing view required logic

We have two methods in presenter which perform certain actions for a view, when view requests them.

We saw that to request something from a presenter the view wouldraise an event and due to the fact that presenter is in its constructorsubscribing to the those events, when the view would send eventpresenter private method would be executed

NOTE: This is standard ‘Fowler like‘ event basedimplementation of the view presenter  communication. The other(simpler) way how this could be implemented is that presenter coulddefine as public methods implementing the logic required by a view andthe view could directlly call them with something like:_presenter.OnUserSave() without having the need for defining events andrelated code concepts. The price of simplicity is that the presenter broke his encapsulation from the view and that the view implementationis now tied to the specific presenter implementation. For a lot ofpeople, this is fair trade off, so in my personal development I amimplementing it like that.
The only reason why I am presenting it using events is to avoid comments that is "not by the book" and "too coupled" :)

OnUserSave method implementation

   1: private void OnUserSave(object sender, EventArgs e)
   2: {
   3:     if (_view.ContextData.IsDirty)
   4:     {
   5:         User user=new User();
   6:  
   7:         User domainUser = DataMapper.Translate(_view.ContextData);
   8:         _userService.SaveUser(domainUser);
   9:     }
  10: }
  11:  

In line 3, we see the presenter reads the context object IsDirtyproperty value to determine if data is changed from the loaded data andif persisting is needed and if the IsDirty is true, in line 7 the DTOobjects is been mapped (translated) to the domain type and the servicepersist method is called after

OnUserSearch method implementation

   1: private void OnUserSearch(object sender, SearchEventArgs e)
   2: {
   3:     if (string.IsNullOrEmpty(e.SearchCriteria))
   4:     {
   5:         _view.StatusMessage = "User name can not be null";
   6:         return;
   7:     }
   8:  
   9:     User user = _userService.GetUser(e.SearchCriteria);
  10:     if (user == null)
  11:     {
  12:         _view.StatusMessage = String.Format(
  13:             "There‘s no user found for user name:{0}", e.SearchCriteria);
  14:         return;
  15:     }
  16:  
  17:     UserDTO userDTO = DataMapper.Translate(user);
  18:     _view.ContextData = userDTO;
  19: }

In line 3 we examine the value of the event parameter caring thelookup user name data and in case of null or empty value in line 5 weset the view interface StatusMessage property value (which would causeon view level setting up the status message) to appropriate errormessage.

In line 9, presenter is accessing the Model through the UserServiceclass GetUser method and in case of not existing user in line 12 setsthe view interface message property to appropriate error message.

Finally, if a user is been successfully retrieved from a userservice, presenter performs in line 17 mapping (translation) of thedomain user data to the DTO type of user data which is then set to theinterface view ContextData property (which would cause on view levelOnDataBound method execution) 

Summary

Supervising Controller pattern is a UI design pattern which takesmost complex responsibilities from the back of the web pages but stillleaving them fair amount of autonomy. That makes it more suitable forimplementing more complex UI pages/controls because having oversizedpresenter and undersized view can sometimes cause in teams overwhelmingof developers working on presenter and leaves underused the one workingon the view

Source code of today‘s example can be found here:  Supervising Controller source code

What is next?

Next part of the MVP close look post series would continue the MVP saga dissecting the Passive Viewvariation of MVP pattern. Some of the classes and concepts presented inthis post would be just referenced in i, so it would definitely be muchshorter :)

Quote of the day:
Another such victory, and we are undone. - Pyrrhus

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
ABAP Objects Design Patterns – Model View Controller (MVC) Part 2
JFace Data Binding Introduction
viewDidLoad和-viewWillApear和-viewDidAppear
SAP CRM Cross Component級別的跳轉(zhuǎn)如果出了問題,該如何調(diào)試
J2ee程序中的面向?qū)ο笤O(shè)計
Applying Strategy Pattern in C Applications...
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服