文档章节

探索MVP(Model-View-Presenter)设计模式在SharePoint平台下的实现

 木宛城主
发布于 2015/03/02 19:37
字数 1894
阅读 6
收藏 0
点赞 0
评论 0

 

对于SharePoint Developers来说,往往会过多的去关注SharePoint平台和工具,而把设计模式和代码的可测试性放在了一个较低的优先级。这并不是说SharePoint Developers对设计模式不感兴趣,而是缺乏在SharePoint平台下使用设计模式的经验。所以本篇Blog正如题目所示:探索MVP(Model-View-Presenter)设计模式在SharePoint平台下的实现。利用MVP设计模式,可以尽量让我们的项目分离关注点、易测试、可重用。在实现MVP时,我也会加入Repository和Service Locator这两种设计模式,Repository可以理解为一个仓储,相当于数据访问层(DAL),而Service Locator扮演了IoC角色,IoC类似一个工厂(容器),工厂内部注册了很多依赖关系,IoC容器正式使用这种依赖关系从而动态的注入(又称依赖注入)提供你所需要的实例,这样可以有效的实现解耦,即分离关注点。

 

MVP模式

在SharePoint平台下,如开发SharePoint Farm Solution,如果不对代码进行重构,往往会出现这样的代码:

很明显这样把所有的逻辑都杂揉在UI Logic,特别是在团队开发时,即不利于测试,也不利于分工协作。而且对于SharePoint而言,开发机性能若低,调试是苦不堪言的,其耗时难以想象。所以前期如能通过单元测试解决Bug,将大大的节约时间。幸运的是,MVP设计模式的出现,对于Web Part的开发,是非常适合的。MVP的特点是很好的分离了关注点,各司其职。把上图稍作更改如下所示:

可以看到的是UI Logic处理的业务逻辑交给了Presenter,而UI彻底解放了,只单纯的做显示层(View)。

 

Repository Design Pattern

从上图可以看出,Presenter并不是直接去访问SharePoint数据层( SharePoint List),而是通过了一个Repository 去间接访问,而Repository Model 封装了数据层。

到这一步,看似完美,但实则还是在原地踏步。因为Presenter和Repository还是紧耦合着,这就好像负责Presenter的 A程序员必须要等负责Repository 的B程序员完成才能工作。

谁叫他们紧耦合在一起呢?

在团队开发中,我们需要的是互相独立,所以需要让负责Presenter的程序员可以使用MockRepository来做测试,这样就不会影响进度了,幸运的是,基于接口的设计,可以让我完成这个愿景。具体的实现如下:

SharePoint Service Locator Design Pattern

仔细分析上图,Presenter还是没有解耦,因为这必须要在Presenter中把某个Repository的实例创建出来,所以Presenter还是依赖了Repository这个项目程序集。这对测试没有好处,(正如前面所分析的那样,开发Presenter 的A程序员必须可以在单元测试里使用MockRepository来测试,而在真实的项目里使用B 程序员开发的AnyRepository)。

那么有没有一种方式能彻底将Presenter和Repository解耦呢?

当然有,如依赖注入,本篇博客介绍的是由Microsoft Patterns and Practices 专门为SharePoint开发的IoC容器:SharePoint Service Locator。

什么是IoC容器

传统的控制流,从客户端创建服务时,必须指定一个特定服务实现(并且对服务的程序集添加引用),IoC容器所做的就是完全将这种关系倒置过来(倒置给IoC容器),将服务注入到客户端代码中,这是一种推得方式(依赖注入)。术语"控制反转",即客户放弃代码的控制,将其交给IoC容器,也就是将控制从客户端代码倒置给容器,所以又有人称作好莱坞原则"不要打电话过来,我们打给你"。实际上,IoC就是使用IoC容器将传统的控制流(客户端创建服务)倒置过来,将服务注入到客户端代码中。

总之一句话,客户端代码能够只依赖接口或者抽象类或基类或其他,而不关心运行时由谁来提供具体实现。

使用IoC容器如SharePoint Service Locator,首先配置依赖关系(即当向Ioc容器询问特定的类型时将返回一个具体的实现),所以这又叫依赖注入。

MVP在项目中的实践

有了上面的分析,那么就来设计漂亮的代码:

  • 模块化代码
  • 松耦合,无依赖
  • 代码重用
  • 独立的单元测试
  •  首先创建IVew,单纯的给UI界面"取"数据和"显示"数据
public interface IEmployeeView
    {
        string Country { get; }
        IEnumerable<EmployeeModel> EmplyeeList { set; }
        bool NotEmployeesFoundMessageVisible { set; }
    }
    • 接着WebPart实现IView
[ToolboxItemAttribute(false)]
    public partial class VisualWebPart1 : WebPart,IEmployeeView
    {
        // Uncomment the following SecurityPermission attribute only when doing Performance Profiling on a farm solution
        // using the Instrumentation method, and then remove the SecurityPermission attribute when the code is ready
        // for production. Because the SecurityPermission attribute bypasses the security check for callers of
        // your constructor, it's not recommended for production purposes.
        // [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Assert, UnmanagedCode = true)]
        private EmployeePresenter _presenter;
        public VisualWebPart1()
        {
            IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(SPContext.Current.Site);
            IEmployeeRepository employeeRepository = serviceLocator.GetInstance<IEmployeeRepository>();
            _presenter = new EmployeePresenter(this, employeeRepository);
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            InitializeControl();
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            _presenter.GetEmployees();
        }

        public string Country
        {
            get { return HttpContext.Current.Request["country"] }
        }

        public IEnumerable<Model.EmployeeModel> EmplyeeList
        {
            set 
            {
                rptDataSource.DataSource = value;
                rptDataSource.DataBind();
            }
        }

        public bool NotEmployeesFoundMessageVisible
        {
            set { lblMessage.Visible = value; }
        }
    }
  • 接着对BaseRepository的设计
public abstract class BaseRepository<T>
    {
        protected SPWeb _web;
        public BaseRepository()
        {

        }
        public BaseRepository(SPWeb web)
        {
            _web = web;
        }
        protected IEnumerable<T> GetEntities(SPListItemCollection items)
        {
            List<T> list =null;
            if (items.Count>0)
            {
                list = new List<T>();
                foreach (SPListItem item in items)
                {
                    list.Add(GetEntity(item));
                }
            }
           
            return list;
        }
        protected abstract T GetEntity(SPListItem item);
    }
  • 正如前面分析的那样,基于接口的设计能更好的做单元测试,所以创建IRepository
public interface IEmployeeRepository
    {
        IEnumerable<EmployeeModel> GetEmployeeByCountry(string country);
    }
  • 实现Repository
public class EmployeeRepository:BaseRepository<EmployeeModel>,IEmployeeRepository
    {
        public EmployeeRepository():base()
        {

        }
        public EmployeeRepository(SPWeb web):base(web)
        {

        }
        public IEnumerable<EmployeeModel> GetEmployeeByCountry(string country)
        {
            SPWeb web = _web ?? SPContext.Current.Web;
            SPList list = web.Lists.TryGetList("Employee");
            IEnumerable<EmployeeModel> employeeEntitiesList = null;
            if (list!=null)
            {
                SPQuery query = new SPQuery();
                query.ViewFields = string.Concat("<FieldRef Name='Title'/>", "<FieldRef Name='CountryField'/>");
                query.ViewFieldsOnly = true;
                if (!string.IsNullOrEmpty(country))
                {
                    query.Query = @"<Where>
                                        <Eq>
                                            <FieldRef Name='CountryField'/>
                                            <Value Type='Lookup'>" + country + @"</Value>
                                        </Eq>
                                    </Where>";
                }
                else
                {
                    query.Query = "";
                }

                SPListItemCollection employeeListColl = list.GetItems(query);
                employeeEntitiesList = GetEntities(employeeListColl);

            }
            return employeeEntitiesList;
        }
        protected override EmployeeModel GetEntity(SPListItem item)
        {

            return new EmployeeModel() {
                Name = item["Title"].ToString(), 
                Country = item["CountryField"].ToString() 
            };
        }
    }
  • 因为Presenter与Repository彻底解耦,故在Presenter中,根据构造函数动态注入View和Repository
public class EmployeePresenter
    {
        private IEmployeeView _view;
        private IEmployeeRepository _repository;
        public EmployeePresenter(IEmployeeView view,IEmployeeRepository repository) 
        {
            _view = view;
            _repository = repository;
        }
        public void GetEmployees()
        {
           string country= _view.Country;
           if (string.IsNullOrEmpty(country))
           {
               return;
           }

           var employees = _repository.GetEmployeeByCountry(country);
           if (HasEmployeeFound(employees))
           {
               ShowEmployees(employees);
           }
           else
           {
               ShowEmployeeNotFoundMessage();
           }

        }
        private void ShowEmployees(IEnumerable<EmployeeModel> employees)
        {
            _view.EmplyeeList = employees;
            _view.NotEmployeesFoundMessageVisible = false;
        }
        private void ShowEmployeeNotFoundMessage()
        {
            _view.NotEmployeesFoundMessageVisible = true;
        }
        private bool HasEmployeeFound(IEnumerable<EmployeeModel> employees)
        {
            if (employees!=null)
            {
                return employees.Count() > 0;
            }
            return false;
        }
    }
  • 关键点来了,在Feature中向SharePoint Service Locator依赖注册(IRepositoy/Repositoy)
public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPSite site = properties.Feature.Parent as SPSite;
            IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(site);
            IServiceLocatorConfig serviceLocatorConfig = serviceLocator.GetInstance<IServiceLocatorConfig>();
            serviceLocatorConfig.Site = site;
            serviceLocatorConfig.RegisterTypeMapping<IEmployeeRepository, EmployeeRepository>();   
        }


         //Uncomment the method below to handle the event raised before a feature is deactivated.

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPSite site = properties.Feature.Parent as SPSite;
            IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(site);
            IServiceLocatorConfig serviceLocatorConfig = serviceLocator.GetInstance<IServiceLocatorConfig>();
            serviceLocatorConfig.Site = site;
            serviceLocatorConfig.RemoveTypeMappings<IEmployeeRepository>();
        }
  • 注意这个Feature 的Scope必须在在Site Level之上(建议在Farm),因为有可能用户在有权限Deactivate Feature
  • 根据依赖关系动态获取实例
private EmployeePresenter _presenter;
        public VisualWebPart1()
        {
            IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(SPContext.Current.Site);
            IEmployeeRepository employeeRepository = serviceLocator.GetInstance<IEmployeeRepository>();
            _presenter = new EmployeePresenter(this, employeeRepository);
        }

 总结

至此,探索MVP(Model-View-Presenter)设计模式在SharePoint平台下的实现,已经全部结束了,在这个基础架构上还可以继续优化,如DataMapper等。相信构建高效清晰整洁的代码是每个程序员所追求的,你不得不佩服国外大神们总结的设计模式是多么的精妙,或许怀着敬畏的心才能慢慢体会其中的奥秘。点击此处下载源代码

 

© 著作权归作者所有

共有 人打赏支持
粉丝 2
博文 222
码字总数 199010
作品 0
黄浦
浅谈MVP与Model-View-ViewModel(MVVM)设计模式

微软的WPF带来了新的技术体验,如Sliverlight、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了诸如Binding、Dependency Property、Route...

高阳
06/26
0
0
Android自定义控件View的探讨

本文转载自:http://www.apkbus.com/forum.php?mod=viewthread&tid=242501&extra=page%3D1 做过了一段时间的安卓开发都会接触到自定义控件,那么对于自定义控件大家都有什么样的看法呢?自定...

谁带我去看看世界
2015/06/24
0
0
iOS架构模式MVC+MVP+mvvm架构

随着iOS职位的火热,越来越多的人都想成为一名优秀的iOS开发工程师,那么在竞争激烈的时代,应该如何成为一名iOS开发工程师呢?现在让大家了解一下iOS架构模式 作为一个开发者,有一个学习的...

iOS晓雯
06/24
0
0
Java 设计模式(14) —— 复合模式

一、复合模式 模式常一起使用,组合在一个设计解决方案中 复合模式在一个解决方案中结合两个或多个模式,能解决一般性或一系列的问题 二、示例 本次设计模式讲解中无代码示例,由于复合模式是...

磊_lei
05/26
0
0
[翻译]MVP(SC),MVP(PV),PM,MVVM 和 MVC 表现模式架构对比

翻译 CodeProject 上的一篇文章,对常见的几种表现模式 (Presentation patterns) 进行了说明,并进行对比。原文地址是http://www.codeproject.com/KB/aspnet/ArchitectureComparison.aspx 表...

吞吞吐吐的
2017/10/11
0
0
代理模式(Mybatis中Mapper动态代理机制)

代理模式 理解动态代理机制前,先理解代理模式。 代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。现实世界的代理人被授权执行当事人的一些事宜,无需当事人出面,从第三方的角...

_八哥_
03/15
0
0
Cocos2d-x设计模式:观察者模式

1、应用场景 前面在介绍单例模式的时候,提到了一个类NotificationCenter,它除了应用单例模式以外,还应用了观察者模式。NotificationCenter类是观察者模式中的目标对象,而NotificationObs...

op_liu
03/22
0
0
总结一些IOS的面试题吧~不定期更新~附带个人理解的“参考”答案哈

除了MVC模式 你还熟悉什么设计模式 代理模式:当一个类的某些功能需要由别的类实现,但是又不确定具体是哪个类实现的 单例模式:当确保程序运行期间,某个类只有一份实例,用于进行资源共享 ...

RainOrz
2015/08/18
0
0
心脏起搏器——MVC的一个简单例子

MVC是多种设计模式结合起来的复合模式。 下面的心脏起搏器是参考《head first设计模式》写得一个mvc小练习。 代码: package heart_driver; public class HeartDriver { public static void ...

leo-H
2012/04/04
0
2
代理模式(Proxy Pattern):动态代理 - 最易懂的设计模式解析

前言 今天我来全面总结开发中最常用的设计模式 - 代理模式中的动态代理模式 其他设计模式介绍 1分钟全面了解“设计模式” 单例模式(Singleton) - 最易懂的设计模式解析 简单工厂模式(Sim...

Carson_Ho
04/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

RabbitMQ实战:5种模式和示例

应用RabbitMQ的5种队列 一、简单队列 P:消息的生产者 C:消息的消费者 红色:队列 生产者实现思路: 创建连接工厂ConnectionFactory,设置服务地址127.0.0.1,端口号5672,设置用户名、密码...

spinachgit
5分钟前
0
0
mysql常见报错标号对应原因以及处理方法

mysql常见报错标号以及对应解决方法 报错标号 报错现象 解决方法 原因 1449 Cause: java.sql.SQLException: The user specified as a definer ('authplat_dev'@'%') does not exist 在控制台...

ChinaHYF
7分钟前
0
0
Java 监控系统技术选型

(1)操作系统监控 Sigar oshi (2)Tomcat监控 JMX 日志 (3)Oracle监控 日志 直连SQL查询 基于Druid连接池

cccyb
9分钟前
1
0
解决IDEA中moduel配置了maven依赖可是依然不能使用依赖中的类

POM.xml中明明配置了依赖,也开启了maven的 auto-import 下面的刷新maven也没用: 直到使用下面的解决办法才使依赖生效: IDEA打开右侧 maven projects 点击顶部的M图表(看下图) 出现如下对...

颖辉小居
10分钟前
0
0
Nginx proxy pass路由转发简单用法

一,在nginx中配置proxy_pass时的加不加/的问题要注意proxy_pass后的url最后的/当加上了/,相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走如果没有/,则会把匹配的...

binhu
10分钟前
0
0
postcss替换sass-loader

实际需求: 已经使用了postcss autoprefixer ,而且sass-loader又依赖于node-sass,而且node-sass又是一堆坑(比如centos升级node6->node8后的安装权限问题),所以想简单使用Postcss 处理s...

吟啸_徐行
12分钟前
0
0
FISCO-BCOS v1.3.1 通过物料包安装记录

本文是从FISCO-BCOS的官方GitHub中的安装包进行安装的记录过程 1. Node.js环境准备 #nodejs安装 nvmsudo curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh......

undefine
16分钟前
0
0
Linux 系统日志、screen 工具

1、Linux 系统日志 日志重要吗?必须的,没有日志我们怎么知道系统状况?没有日志如何排查一个trouble?日志记录了系统每天发生的各种各样的事情,你可以通过它来检查错误发生的原因,或者受...

JolieLin
26分钟前
1
0
MAVEN打包时报错“程序包xxx不存在”

1.错误场景: 项目为springboot项目,maven聚合工程,分为app、api 、common、gongqiu四个项目,app、api 、common的父类是gongqiu,app和api依赖common,打包时报错common中的程序包xxx不存...

无语年华
28分钟前
0
0
CSS

一、简介 CSS 指层叠样式表 (Cascading Style Sheets) 样式定义如何显示 HTML 元素 样式通常存储在样式表中 把样式添加到 HTML 4.0 中,是为了解决内容与表现分离的问题 外部样式表可以极大提...

blackfoxya
36分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部