序: 让我们首先通过现实的例子来看看 Model、View、Presenter 应该如何分工吧。View 就像是客服人员(或者留学中介里的顾问),Model 是那些具体的技术支持人员(或者文案,专门处理签证申请材料),Presenter 是组长或部门经理。

View 不需要做太多的具体事情,他们最好相貌好点,声音甜点,对用户友好点,让用户心情舒畅就好,用户的最终问题最终还是由具体的技术人员(技术支持,售后)处理。对于个体户,小作坊,工作室来说,客服、技术可能一人包了,这样效率最高,但后来你发现所有问题都你一个人处理,一个人不可能三头六臂,事必躬亲最终只能做作坊;这是单层结构。

所以你招聘了新的员工让他们负责具体技术问题处理,你专心做客服,这样你能承接的业务量大了,当然代价是工作效率低了成本高了,但这是做大必须要付出的代价;这是两次结构。

后来你的公司规模越来越大,你就发现人员之间关系复杂,相互之间依赖严重,管理起来头绪繁多;并且分工不明确,权责不分明,扯皮时有发生,很多问题接踵而来。现在你才发现,管理不正规化是很难做大做强的,因此,你进行了组织结构调整并建立起专业化的互补团队,他们各司其职,你只要管理好你的经理层,整个企业就能做的很好。每个部门都有自己明确的职责,每个部门内部都有几个相互合作的小组或团队,部门经理的核心工作就是协调,让合适的人(团队)做合适的事情。同时不鼓励培养全能型员工,因为不可能所有的员工都能全能,而依赖于少数全能员工无疑是有风险的,所以尽量分工细化,尽量使员工的工作简单(这样很容易找到合适的替换人员),尽量培养互补的专业化人才。

这样整个公司就比较和谐,代价就是沟通成本更高了,公司仅仅因此付给管理层的薪水就很可观,但没办法,要做世界 500 强,不舍得出血是不可能的。

当然,并不是付了这些成本就可以做世界 500 强,要想做好首先要规划好,应该如何分工应该如何协作,其次要找好管理者协调好各部门各组织的工作,否则不但增加了成本反而会使整个系统更混乱。

同样的道理,能不能做好一个系统与使不使用 MVP 或者 MVC 这些模式其实没有必然的关系,并不会应为使用了 MVP 项目就会成功,同样也不能将项目失败的原因归结到 MVP 或者 MVC 这些模式,而说它们不好。谋事在人,成事也在人。能否领会到 MVP 的优缺点,能否准确的判断你的团队能否驾驭 MVP 才是关键。

一、View 设计参考

View 和 Presenter 的设计应该以用例(Use Case)为基础,以面向对象设计原则为准则。StopLight 和 BankBranchWorkbench 参考实现就是根据用例进行设计的。

根据面向对象的设计原则,对象的职责要明确单一(强内聚),对其他对象的细节了解的越少越好,尽量的只需要关心别人的接口,而不必关心别人的实现。StopLight 的设计可以给我一些启示:

StopLight 的 View 有 StopLightView 是 IStopLightView 组成。

IStopLightView 接口定义了 View 在该用例中具有的能力,IStopLightView 的职责是告诉 Presenter 我能够提供什么服务,你能够从我这获得什么信息:


    public interface IStopLightView : INotifyPropertyChanged
    {
        Color CurrentColor { get; set; }
        string GreenDuration { get; set; }
        string YellowDuration { get; set; }
        string RedDuration { get; set; }
        event EventHandler UpdateClicked;
        event EventHandler ForceChangeClicked;
        void SetError(string propertyName, string errorMessage);
     } 

也就是说 View 的实现应该允许我们设置并获取当前显示的颜色, 运行我们设置并获取每种颜色的显示时间间隔,运行我们告诉它有错误发生(具体它如何处理该错误信息我们不关心),同时在通供用户接口并且在用户操作后通知我们,具体我们要做什么,View 接口也不关心。

StopLightView 是 IStopLightView 接口的具体实现,它继承自 UserControl ,因此是 WinForm 类型的界面。作为用户控件对象,其核心职责是表现,给用户提供友好的用户接口,包括美观的布局和方便的操作,而不用关心业务逻辑。

在实现 IStopLightView 接口方面,StopLightView 始终恪守本分,不做任何与自己无关的事情,只负责将自身包含的子控件属性的读写(包括 ErrorProvider),提供事件注册接口,并在属性更改时发布通知(利用事件实现发布订阅模式)。

通过阅读 StopLightView 的代码我们看到,虽然 StopLightView 强依赖于 StopLightViewPresenter ,但整个 StopLightView 只使用了 StopLightViewPresenter 的 View 属性和 OnViewReady() 方法,而这两个方法(属性)都是抽象类 Presenter<TView> 已经定义的,也就是说即使我们没有对 StopLightViewPresenter 做任何开发,StopLightView 也是可以编译通过的。

因此 IStopLightView 接口只负责提供属性,事件定义和操作接口,这些和界面相关,是用户接口(UI)的职责范围,但 IStopLightView 对具体的实现形式及页面布局没有任何要求,我们可以使用 Winform 实现,也可以使用 WPF、SilverLight,甚至我们可以使用 ASP.NET 或者 AJAX 来实现,实现者也不会有对界面布局方式,操作方式的约束,只要他们能够实现 IStopLightView 接口就一切 OK 。

这就是职责单一!这就是松耦合!这给我们以后维护界面,更改界面带来了极大的方便;同时,对于团队开发,我们完全可以把 UI(UX) 设计与程序业务逻辑设计分开,也因此方便了我们对 UI 及业务逻辑的协作开发和分离测试。

二、Presenter 设计参考

在前一篇中,我们已经简单的看了 Presenter 对 MVP 架构的支持,现在我们再仔细的看看 Presenter<TView> (public abstract class Presenter<TView> : IDisposable)。

所有具体的 SCSF Presenter 都应该继承自抽象基类  Presenter<TView> 。该抽象基类提供了 MVP 模式的基本框架,是 MVP 模式的核心。

Presenter 的主要职责就是用于协调 View 和 Model,在 StopLight 项目中是通过依赖注入与 Model 建立了联系,而在典型的 SCSF 中,Presenter 与 Model 的关系应该通过 WorkItem 建立 。

每个 Presenter 都有一个当前 WorkItem 实例:


    [ServiceDependency]
    public WorkItem WorkItem
    {
        get { return _workItem; }
        set { _workItem = value; }
    } 

[ServiceDependency] Attribute 告诉 ObjectBuilder 把环境中的当前WorkItem 实例附给当前 Presenter 的 _workItem 字段。以后任何 Presenter 实例都可以通过当前的 WorkItem 获取需要的资源,包括已注册的 Services,SmartParts,Commands,State,UIExtentions 等。

Presenter<TView> 中还有三个虚方法,两个 public 一个 protected 。


        /// <summary>
        /// 在具体 View 的 OnLoad 方法中调用
        /// </summary>
        public virtual void OnViewReady() { }
        /// <summary>
        /// 在 TView 被设置时调用
        /// </summary>
        protected virtual void OnViewSet() { }
        /// <summary>
        /// 在 Dispose 中调用
        /// </summary>
        public virtual void OnCloseView() { }

具体 Presenter 子类可以重写上面三个方法来做必要的工作(这与 Form 编程中的 OnLoad等方法类似,很容易理解)。

在执行顺序上, OnViewSet() 在 Presenter 类设置 View 属性时最先被调用:


        // Presenter<TView> 类中的 View 属性,set 时调用 OnViewSet() 方法
        public TView View
        {
            get { return _view; }
            set { _view = value; OnViewSet(); }
        } 

根据前面讲的,View 被创建时会自动创建对应的 Presenter 并将 View 实例附给该 Presenter,因此 OnViewSet() 在 View 被创建后初始化时调用,这时 View 和 Presenter 都已经创建,可以做进一步初始化。StopLight 项目中的 StopLightViewPresenter 重写了 OnViewSet() 方法,在里面调用了私有方法 wireEvents() :


        private void wireEvents()
        {
            View.PropertyChanged += OnViewPropertyChanged;
            View.UpdateClicked += OnViewUpdateClicked;
            View.ForceChangeClicked += OnViewForceChangeClicked;
            Schedule.ChangeLight += delegate { Stoplight.Next(); };
            Stoplight.Changed += OnStoplightChanged; 
        } 

该方法将事件处理器和事件发布者进行了绑定,这是构建松散耦合应用的另一重要方式。

OnViewReady() 方法在具体 View 的 OnLoad 方法中调用:


        /// <summary>
        /// 在 View 加载时允许 Presenter 注入适当的操作。开发者可以在 Presenter 中重写 OnViewReady() 介入视图加载过程。
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            _presenter.OnViewReady();
            base.OnLoad(e);
        } 

OnLoad 在第一次显示窗体前调用,这时基本的初始化工作可以完成,子程序可以做进一步的初始化,如分配控件使用的资源。StopLightViewPresenter 重写了 OnViewReady () 方法,在里面调用了私有方法 initViewAndStart():


        private void initViewAndStart()
        {
            View.GreenDuration = "3000";
            View.YellowDuration = "500";
            View.RedDuration = "5000";
            View.CurrentColor = Color.Green;
            Schedule.Update(TimeSpan.FromMilliseconds(3000),
                         TimeSpan.FromMilliseconds(500),
                         TimeSpan.FromMilliseconds(5000));
            Schedule.Start();
        } 

至此,定时器开始工作,业务流程正式启动。

每个用例都对应一组 View 和 Presneter 。 Presenter 是整个用例的大管家,控制协调中心,自己基本上不做具体事情,核心工作都是组织和协调。作为控制器,Presenter了解与该用例相关的所有事情,因此能够将合适的事情指派给合适的人去处理。

三、Model 设计参考

上一篇说过,Model 在程序中是另一个被滥用的名词,很多东西都可以归为 Model ,我们这里把 Model 认为是与实际业务模型对应的程序模型,这些模型是对现实世界业务的建模。在 SCSF 中,Model 通常可以用一系列的 Services 来表示。

模型中对现实世界的实体进行的建模一般是比较稳定的,因此这部分变化小;现实中业务规则还有人们对业务规则的理解往往是变动的,我们通过 Presenter 来应对这种变化,这样 Model、 Prestener、View 都可以通过这种松散耦合来达到开闭原则,从而使我们的系统有步骤的强大。因为开闭原则意味著我们原先做的工作都是有意义的,不会因为局部的改变而是我们推到重来。

获取这种松散耦合的优势最主要得益于面向对象语言的多态特性和里氏代换原则,当然这种优势背后的代价就是使我们多写了不少代码,使程序对于一般人来说似乎变得复杂。

Model、View 、Presenter 设计要以用例(Use Case)为基础,以面向对象设计原则为准则。力求职责单一,有选择的面向抽象,构建易测试、易理解的松散耦合系统。

仔细想想 MVP 模式(或者 MVC)的分工与现实世界的分工原来是如此的一致,如此的和谐。View 就像是前台的客服人员,Model 是实际的技术支持人员,Presenter 是他们的主管,这样势必会增加沟通的成本,但对于大公司这种成本是必须的,否则永远不可能最大做强。

转载于:https://www.cnblogs.com/jcomet/archive/2013/05/29/3105858.html

SCSF 系列:Smart Client Software Factory 中 MVP 模式最佳实践相关推荐

  1. SCSF 系列:Smart Client Software Factory 中的 MVP 模式概述

    Smart Client Software Factory 是一个关注 Smart Client (智能客户端)构建的 UI 层框架,提供了对 MVP 模式的 First Class 支持,不了解 M ...

  2. SCSF 系列:Smart Client Software Factory 与 ObjectBuilder

    [FLYabroad]ObjectBuilder 简介,SCSF 对 ObjectBuilder 的使用和扩展,SCSF 与控制反转(IOC). 上一篇:Smart Client Software F ...

  3. Smart Client Software Factory 初试

    Smart Client Software Factory 初试 介绍 智能客户端的介绍我就不再这里说明了,大家可以通过Google去发现. 智能客户端软件工厂提供给建筑师和开发商能够快速综合智能客户 ...

  4. Smart Client Software Factory安装

    首先要安装 Visual Studio 2010 SDK 不然无法安装 Smart Client Software Factory 2010 然后按顺序安装 GAX 2010 http://visua ...

  5. SCSF 系列:Smart Client Software Factory 启动过程详解

    应网友要求,结合参考实现(BankBranchWorkbench)写一篇关于 SCSF 内部工作原理的文章,需要读者有 SCSF 基础.基本概念和基本理念后面相关文章介绍. SCSF 自动为我们建立了 ...

  6. Web Client Software Factory系列(4):数据绑定和ObjectContainerDataSource控件

    概述 在Web Client Software Factory系列(3):View-Presenter模式中提到,表示器包含了响应用户事件逻辑以及一些View的状态等,在Web Client Soft ...

  7. Web Client Software Factory系列(3):View-Presenter模式

    概述 将一个ASP.NET站点分离为多个独立的模块,一个最大的问题就是与页面相关联的大多数业务逻辑驻留在该页面的源代码文件中,我们几乎做不到将源代码文件分为多个独立的程序集.为了真正创建独立的与站点中 ...

  8. 关注 Web Client Software Factory [Weekly Drop 08]

    微软模式与实践小组将于今年12月中旬发布的Web Client Software Factory是非常值得我们期待的一个项目,它提供了一个Software Factory来指导我们如何使用微软的平台( ...

  9. android mvp模式例子_Android中mvp模式使用实例详解

    MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新的模式,MVP与MVC有着一 ...

最新文章

  1. python输入与输出165
  2. Eclipse小技巧
  3. client-go入门之2:Job相关操作
  4. WebGL学习笔记七点一
  5. 安卓APP_ 布局(1)—— LinearLayout
  6. MaskFusion:惊艳的结合实例感知、语义分割、动态追踪的SLAM系统
  7. [Ajax] jQuery中的Ajax -- 01-jQuery中的Ajax
  8. 路由器下接路由器设置方法(路由器级联)
  9. x264 编码器选项分析 (x264 Codec Strong and Weak Points) 1
  10. 框架 go_Go语言优秀应用开发框架 GoFrame
  11. c语言人工智能程序,怎么程序编程语言 怎么用C语言编写人工智能程序?
  12. 使用librtmp接收数据时要注意的问题
  13. java计算机毕业设计ssm拼团旅游系统element 前后端分离
  14. 解析TCP/UDP协议的通讯软件
  15. RxAndroid结合Retrofit,看看谁才是最佳拍档!
  16. 哪些股票自动交易接口好用呢?
  17. iOS多线程之GCD
  18. Nature Neuroscience:怀孕导致人类大脑结构的长久改变
  19. 计算机网络波动大,网络不稳定怎么办,小编教你电脑网络不稳定怎么办
  20. javaScript实现通过鼠标滑轮改变元素大小

热门文章

  1. Highcharts JS去除Highcharts.com链接的方法
  2. python中int函数规则_python数字规则和内建函数
  3. python实现不重复排列组合_Python实现输入字符串,返回其任意排列组合
  4. mysql explain理解
  5. 第一个 PyQt5程序
  6. Qt C++属性类型提供给 QML调用(五)
  7. while(1); 作用
  8. Javaweb基础——Servlet
  9. ftp、sftp、vsftp、ssh、vsftpd、sshd
  10. C/Cpp / 设计模式 / 简单工厂模式