MVP之V和P的交互
三者之间的关系
在MVP初探里简单的描述了V和P之间是如何交互的。
无论是PV还是SC,M\V\P这三者之间的关系并没有发生改变,V只是前端的客户代理承现展显数据,P是如何处理客户交互行为的决策者。
数据是P主动“推”给V的,而V只向P发送用户通知,都是单向的;所以在IView中被Presenter调用的方法应该都是没有返回值的。可以通过调用IView中的方法,也可以通过事件注册的方式来实现P/V之间的交互。
那么,在PV模式下View是被动的,而P则控制着对V请求的处理;view仅将用户的(事件)请求提交到P,它并不参与对用户请求的处理,如果在这个过程中P要V参与(如:显示实时的数据等)也是P将要展现的数据PUSH到V,对于处理的结果的呈现也是同样的处理方式。
而P在此过程中的角色是一个决策者,它决定如何处理V提交过来的用户请求,如何去实现业务逻辑和调用Model。
如何维护三者的关系
View仅是传递用户请求和对数据的呈现;Presenter是事件的的决策者;Model业务逻辑执行者。
在PV中三者的关系使用的uView可以直接调用Presenter,这样就会使用的在开发过程中将Presenter作为View和Model之间的一种代理(Proxy)而不是Presenter.
那么我们就可以通过事件订阅机制来解决这个问题。
编程模型的实现
首先,要让View不能访问到Presenter,我们可以通过事件订阅机制;为所有的Presenter创建基类Presenter<IView>,泛型类型IView表示具体View实现的接口。表示View的同名只读属性在构造函数中赋值,赋值完成之后调用调用虚方法OnViewSet,使得对具体的Presenter可以重写该方法进行对View进行事件注册工作。
1 namespace Handwe.Demo.MVP 2 { 3 public class Presenter<IView> 4 { 5 public IView View { get; set; } 6 7 public Presenter(IView view){ 8 this.View = view; 9 this.OnViewSet(); 10 } 11 12 protected virtual void OnViewSet() 13 { 14 15 } 16 } 17 }
其次,Presenter是通过接口的方式与View进行交互的。有时候我们要通过这个接口访问Form的一些属性、方法和事件,需要将相应的成员定义在接口上面,可能会有很多,添加的删减等。那么,我们可以将这些成员定义在一个接口中,具体View的接口继承该接口就可以了。在这里,我们相当是为所有的View接口创建了“基接口”。如下:
1 namespace Handwe.Demo.MVP 2 { 3 public interface IViewBase 4 { 5 event EventHandler Load; 6 event EventHandler Closed; 7 event CancelEventHandler Closing; 8 9 string ResultText 10 { 11 get;set; 12 } 13 14 } 15 }
同时,也要创建所有的View创建基类ViewBase,做必要的初始化工作,如加载基础数据等:
1 namespace Handwe.Demo.MVP 2 { 3 public class ViewBase : Form 4 { 5 public ViewBase() 6 { 7 this.CreatePresenter(); 8 } 9 10 protected virtual object CreatePresenter() 11 { 12 if (LicenseManager.CurrentContext.UsageMode == LicenseUsageMode.Designtime) 13 { 14 return null; 15 } 16 else 17 { 18 throw new NotImplementedException(string.Format("{0} must override the CreatedPresenter method.", this.GetType().FullName)); 19 } 20 } 21 } 22 }
在ViewBase中调用CreatedPresenter(),它的实现在具体的每个View中也就是实现Presenter和View对应;
1 public partial class CalculatorView : ViewBase, ICalculatorView 2 ... 3 protected override object CreatePresenter() 4 { 5 return new CalculatePresenter(this); 6 } 7 8 ... 9 }
实例的实现:
那边现在可以定义View的接口ICalculatorView,在这里定义了一个事件Calculating,也就是点击btnCalculate触发,还要定义三个方法,DisplayResult(int result)、DisplayMsg(stirng msg)、还有一个是Clear();具体什么用途看名称就知道了,如下:
1 namespace Handwe.Demo.UnityInMVP 2 { 3 public interface ICalculatorView : IViewBase 4 { 5 event EventHandler<CalculateEventArgs> Calculating; 6 7 8 void DisplayResult(int result); 9 10 void DisplayMsg(string msg); 11 12 void Clear(); 13 } 14 }
这里有一个CalculateEventArgs,它是自定义的事件参数类型:
1 namespace Handwe.Demo.UnityInMVP 2 { 3 public class CalculateEventArgs : EventArgs 4 { 5 public string OperationNum1 { get; set; } 6 7 public string OperationNum2 { get; set; } 8 } 9 }
很简单只定义了两个属性;
接着来实现CalculatorView,代码如下:
1 namespace Handwe.Demo.UnityInMVP 2 { 3 public partial class CalculatorView : ViewBase, ICalculatorView 4 { 5 public CalculatorView() 6 { 7 InitializeComponent(); 8 } 9 10 public string ResultText 11 { 12 get { return this.labResult.Text; } set{this.labResult.Text = value;} 13 } 14 15 protected override object CreatePresenter() 16 { 17 return new CalculatorPresenter();
18 } 19 20 #region ICalculateView Members 21 public event EventHandler<CalculateEventArgs> Calculating; 22 23 public void DisplayResult(int result) 24 { 25 this.textBoxResult.Text = result.ToString(); 26 } 27 28 public void Clear() 29 { 30 this.textBoxOp1.Text = string.Empty; 31 this.textBoxOp2.Text = string.Empty; 32 33 this.DisplayMsg(string.Empty); 34 } 35 36 public void DisplayMsg(string msg) 37 { 38 this.labDisplayMsg.Text = msg; 39 } 40 #endregion 41 42 private void btnCalculate_Click(object sender, EventArgs e) 43 { 44 string op1 = this.textBoxOp1.Text.Trim(); 45 string op2 = this.textBoxOp2.Text.Trim(); 46 this.OnCalculating(op1, op2); 47 } 48 49 private void btnClear_Click(object sender, EventArgs e) 50 { 51 } 52 53 private void btnCheck_Click(object sender, EventArgs e) 54 { 55 } 56 57 protected virtual void OnCalculating(string op1, string op2) 58 { 59 if (null != this.Calculating) 60 { 61 this.Calculating(this, new CalculateEventArgs { OperationNum1 = op1, OperationNum2 = op2 }); 62 } 63 } 64 } 65 }
然后我们实现事件的订阅者,这里就是CalculatePresenter:
1 namespace Handwe.Demo.UnityInMVP 2 { 3 public class CalculatePresenter : Presenter<ICalculatorView> 4 { 5 public CalculatePresenter(ICalculatorView view):base(view) 6 { 7 this.Calculate = new Calculate(); 8 } 9 10
11 public Calculate Calculate 12 { 13 get; 14 set; 15 } 16 17 protected override void OnViewSet() 18 { 19 this.View.Load += (sender, args) => 20 { 21 22 this.View.Clear(); 23 }; 24 25 this.View.Calculating += (sender, args) => 26 { 27 int op1, op2; 28 if (!int.TryParse(args.OperationNum1, out op1)) 29 { 30 this.View.DisplayMsg(string.Format("{0} type invalid.", args.OperationNum1)); 31 return; 32 } 33 34 if (!int.TryParse(args.OperationNum2, out op2)) 35 { 36 this.View.DisplayMsg(string.Format("{0} type invalid.", args.OperationNum1)); 37 return; 38 } 39 int result = this.Calculator.Divide(op1, op2); 40 41 this.View.DisplayResult(result); 42 43 this.View.Clear(); 44 this.View.DisplayMsg("please type operation numbers."); 45 }; 46 } 47 } 48 }
至此,Calculator演示实例已基本完成了,下篇将会在这个实例的基础上引入Unity、PIAB、Exception Handling,实现对Calculate的解耦和异常处理。《Unity、PIAB、Exception Handling引入MVP》
http://www.cnblogs.com/coble/p/5646717.html