zoukankan      html  css  js  c++  java
  • MVP培训总结

      首先简单说一下MVP,对于一个UI模块来说,它的所有功能被分割为三个部分,分别通过Model、View和Presenter来承载。Model、View和Presenter相互协作,完成对最初数据的呈现和对用户操作的响应,它们具有各自的职责划分。Model可以看成是模块的业务逻辑和数据的提供者;View专门负责数据可视化的呈现,和用户交互事件的相对应。一般地,View会实现一个相应的接口;Presenter是一般充当Model和View的纽带。在MVP中,应用程序的逻辑主要在Presenter来实现,View是很薄的一层,能够把信息显示清楚即可,View只应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不应该有更多的内容。

    下面就一个简单的例子来看看具体的MVP模式是如何实现的:

    假设我们要实现如下功能:

    从图中可以看到该模块的功能很简单,用户添加新的User点击Save后系统保存并刷新上方列表。

    首先隔离功能,构建两个用户控件UserAdd和UserList

    这样分开两个用户控件是为了实现功能的分离UserAdd只负责收集用户输入,UserList只负责显示数据列表

    接着我们看看UserAdd的代码实现:

    从代码中可以看到UserAdd的功能非常简单、单一,只负责显示UserName和UserAge,接受了用户的Click事件也不处理而是公开了一个UserAddEvent让外部去实现。

    UserAdd
    public partial class UserAdd : UserControl, IUserAdd
        {
            public event EventHandler UserAddEvent;
    
            public string UserName
            {
                set { this.txbName.Text = value; }
                get { return this.txbName.Text; }
            }
    
            public string UserAge
            {
                set { this.txbAge.Text = value; }
                get { return this.txbAge.Text; }
            }
    
            public UserAdd()
            {
                InitializeComponent();
            }
    
            private void btnAdd_Click(object sender, EventArgs e)
            {
               if (UserAddEvent != null) UserAddEvent(this, e);
            }
        }

    代码中可以看到,UserAdd实现了一个接口IUserAdd

    IUserAdd
    1 public interface IUserAdd
    2     {
    3         event EventHandler UserAddEvent;
    4 
    5         string UserName { get; set; }
    6 
    7         string UserAge { get; set; }
    8 
    9     }

    抽象出一个接口是为了,以后Presenter依赖抽象即依赖接口而不是依赖特定的UserAdd类。
    然后对UserList也可以进行相似的抽象实现。

    Model用于维护User列表信息,下面来看一下Model的实现:

    UserModel
     1 public class UserModel:IUser
     2     {
     3         private readonly IList<User> _users = new List<User>();
     4 
     5         public UserModel()
     6         {
     7             // generate some test data            
     8             AddItem(new User { Name = "Peter", Age = 29 });
     9         }
    10 
    11         /// <summary>
    12         /// Return data property cast to proper type
    13         /// </summary>
    14         public IList<User> Users
    15         {
    16             get { return _users; }
    17         }
    18 
    19         /// <summary>
    20         /// add an item to the data
    21         /// </summary>
    22         /// <param name="user"></param>
    23         public void AddItem(User user)
    24         {
    25             Users.Add(user);
    26         }
    27 
    28         /// <summary>
    29         /// update an item in the data
    30         /// </summary>
    31         /// <param name="user"></param>
    32         public void UpdateItem(User user)
    33         {
    34             for (int i = 0; i < Users.Count; i++)
    35             {
    36                 if (Users[i].Name.Equals(user.Name))
    37                 {
    38                     Users[i] = user;
    39                     break;
    40                 }
    41             }
    42         }
    43 
    44         /// <summary>
    45         /// delete an item in the data
    46         /// </summary>
    47         /// <param name="user"></param>
    48         public void DeleteItem(User user)
    49         {
    50             for (int i = 0; i < Users.Count; i++)
    51             {
    52                 if (Users[i].Name.Equals(user.Name))
    53                 {
    54                     Users.RemoveAt(i);
    55                     break;
    56                 }
    57             }
    58         }
    59     }

    我们也为UserModel抽象出一个接口:

    IUserModel
    1     public interface IUser
    2     {
    3         IList<User> Users { get; }
    4         void AddItem(User user);
    5         void UpdateItem(User user);
    6         void DeleteItem(User user);
    7     }

    这样做的目的也是为了使Presenter依赖于接口而不是依赖于UserModel类。

    好了View和Model都实现好了,下面要实现的是最关键的Presenter了,需要通过Presenter将View和Model联系起来。

    先来看UserAddPresenter的实现:

    UserAddPresenter
     1 public class UserAddPresenter:IPresenter
     2     {
     3         public IUserAdd _userAdd;
     4         public IUserModel _userModel;
     5         public UserAddPresenter(IUserAdd userAdd, IUserModel userModel)
     6         {
     7             this._userAdd = userAdd;
     8             this._userModel = userModel;
     9             this._userAdd.UserAddEvent += UserAddResponse;
    10             //初始化时将此Presenter加入到MessageCenter的PresenterList上
    11             MessageCenter.SinlgeInstance.AddPresenter(this);
    12         }
    13         public void UserAddResponse(object sender, EventArgs e)
    14         {
    15             _userModel.AddItem(
    16                 new User()
    17                     {
    18                         Name = _userAdd.UserName, Age = Convert.ToInt32(_userAdd.UserAge)
    19                     });
    20             SendMessage(OperationType.Add);
    21         }
    22         public void SendMessage(OperationType operationType)
    23         {
    24 
    25             MessageCenter.SinlgeInstance.CallPresenters(operationType);
    26             
    27         }
    28         public void ResponseCall(OperationType operationType)
    29         {
    30             
    31         }
    32     }

    代码中可以看到UserAddPresenter中将IUserAdd,IUserModel联系起来。
    this._userAdd.UserAddEvent += UserAddResponse;

    此Presenter中实现了IUserAdd.UserAddEvent也就是说UserAdd接收到了客户的按钮点击 具体的逻辑实现UserAdd就不管了,而是在此交给了UserAddPresenter来处理。

    可以看得到UserAddPresenter首先调用了Model将数据保存了起来,随后调用SendMessage发送了一条消息。

    现在我们想要用户点击Save时保存用户输入的数据这一步已经实现,如何能实现刷新用户列表呢?我们肯定要有个地方通知到UserList。

    我们调用了MessageCenter.SinlgeInstance.CallPresenters那下面我们看看MessageCenter中做了什么。

    MessageCenter
     1 public class MessageCenter
     2     {
     3         private MessageCenter()
     4         {
     5             PresentersList = new List<IPresenter>();
     6         }
     7         private static MessageCenter instance;
     8         private static object lockObj=new object();
     9         public static MessageCenter SinlgeInstance
    10         {
    11             get
    12             {
    13                 if (instance == null)
    14                 {
    15                     lock (lockObj)
    16                     {
    17                         if (instance == null)
    18                         {
    19                             instance = new MessageCenter();
    20                         }
    21                     }
    22                 }
    23                 return instance;
    24             }
    25         }
    26         protected IList<IPresenter> PresentersList
    27         {
    28             get;
    29             set;
    30         }
    31         public void AddPresenter(IPresenter presenter)
    32         {
    33             PresentersList.Add(presenter);
    34             
    35         }
    36         public void RemovePresenter(IPresenter presenter)
    37         {
    38             PresentersList.Remove(presenter);
    39         }
    40         public void CallPresenters(OperationType operationType)
    41         {
    42             foreach (IPresenter presenter in PresentersList)
    43                 presenter.ResponseCall(operationType);
    44         }
    45     }

    可以看出来MessageCenter中维护了一个PresentersList,当有消息发来时MessageCenter就会遍历PresentersList发送消息,谁需要对这个消息进行处理即自行处理,不需要的话直接忽略即可。
    这有点类似于,员工小王用OutLook给公司所有人发了一封关于设计模式培训的邮件,他并不知道"所有人"里都包含哪些人,他只需要在收件人里写上AllUsers,点发送即可具体谁会收到谁想处理已经和小王无关,消息中心收到消息后,会遍历公司所有人并将邮件发给他们,所有人都会收到邮件,然而具体怎么处理是他们自己的事,测试人员可能会直接忽略这封邮件,而开发或设计人员可能会在日程上进行标记以便能准时参加培训。

    来看看UserListPresenter的实现吧:

    UserListPresenter
     1     public class UserListPresenter : IPresenter
     2     {
     3         public IUserModel _userModel;
     4         public IUserList _userList;
     5         public UserListPresenter(IUserList userList, IUserModel userModel)
     6         {
     7             this._userList = userList;
     8             this._userModel = userModel;
     9             this._userList.StartLoadEvent += UserListLoad;
    10             MessageCenter.SinlgeInstance.AddPresenter(this);
    11         }
    12         public void UserListLoad(object sender, EventArgs e)
    13         {
    14             _userList.Users = _userModel.Users;
    15         }
    16         public void SendMessage(OperationType operationType)
    17         {
    18             MessageCenter.SinlgeInstance.CallPresenters(operationType);
    19         }
    20         public void ResponseCall(OperationType operationType)
    21         {
    22             switch (operationType)
    23             {
    24                 case OperationType.Add:
    25                     _userList.Users = _userModel.Users;
    26                     break;
    27                 default:
    28                     break;
    29             }
    30         }
    31     }

    可以看到ResponseCall方法中对Add消息进行了处理即刷新列表。 每个Presenter都需要有一个ResponseCall方法,所以抽象出接口IPresenter

    IPresenter
    1 public interface IPresenter
    2     {
    3         void ResponseCall(OperationType operationType);
    4     }

    这样的话在MessageCenter就可以遍历调用IPresenter的ResponseCall方法了。

    这样做的好处是,在设计每个Presenter的时候,不必关心它以后会被谁调用,只需要处理好自己的逻辑以及对感兴趣的消息的处理。

    至此,这个简单的示例就讲解完毕了。

    总结一下MVP,Model负责数据的维护,View只负责显示数据,Presenter就是将View和Model联系起来,而Presenter之间的交互也不是直接交互而是通过MessageCenter进行联系。

    采用MVP模式实现的项目很容易进行单元测试,因为处理逻辑主要集中在Presenter中,而Presenter与Model和View的关联也都是通过接口,这样就可以在Model和View都没有完成的情况下对Presenter进行单元测试。

    本人初识此模式,在此也只是将自己的一些培训所得和肤浅总结记录一下而已,纰漏之处欢迎各位高手指正。

  • 相关阅读:
    Unity3d资源管理分析
    Unity3D之MonoBehaviour的可重写函数整理
    Unity3D脚本学习——运行时类
    A*寻路初探(转载)
    360sdk网游支付服务
    总结使用Unity 3D优化游戏运行性能的经验
    Unity3D之Lightmap详解
    「luogu2714」四元组统计
    「BZOJ1066」[SCOI2007]蜥蜴
    「luogu1251」餐巾计划问题
  • 原文地址:https://www.cnblogs.com/xg521310/p/2676476.html
Copyright © 2011-2022 走看看