作者:Kevin Zou 来源:博客园   酷勤网收集 2008-11-17

摘要
  权限与业务不要耦合,因为两者实际上关系不大,有无权限,并不会决定这个程序的执行过程。一直以权限设计作为解耦的故事主题,但是最重要的还是一个设计思想,不单是权限的问题。

上文回顾:《解耦的故事:权限设计

在上篇解耦的故事中,我把权限设计分成了2类,一类是以系统功能为出发点管控系统的访问权限,并且将页面的权限功能模块与业务模块解耦。很多网友提出第2类的数据权限管控比较重要,所以今天就第2类权限的设计思想再详细描述一下。

以一个例子开始

类似在人事考勤查询中,不同人员能够查询的人事考勤信息应该是不一样的,如只可以查到自己部门的人员信息。

于是可能在代码中会这样写到:

Dept dept = GetUserDept(CurrentUserID);         //获取当前登录用户的部门

DataTable dt = Query(dept);                //按照这个部门查询人事考勤信息

dataGrid1.DataSource = dt;

dataGrid1.DataBind();                     //绑定DataGrid,显示在网页上

很快user就会把需求变更到,管理员可以查所有部门的人员信息。有的主管管多个部门,可以查询到他所管理的部门的人员信息。显然,这个时候使用当前登录用户的部门来作为查询依据已经无法满足了,于是,权限部门的概念就呼之欲出。即给用户分配部门,他有几个部门,他就能查询到几个部门的信息。代码当然很简单,如下:

List<Dept> deptList = GetUserRightDepts(CurrentUserID);  //获取当前登录用户的权限部门

dropdownlist1.DataSource = deptList;    //绑定部门下拉框(user只能在权限部门里面选择,并查询)

dropdownlist1.DataTextField = "Dept_Name";

dropdownlist1.DataValueField = "Dept_No";

dropdownlist1.DataBind();

可能很多系统设计师到这里也就结束了,最多封装成一个控件,然后每次使用时直接拖到aspx页面,这样做也行,不过无论如何,还是觉得别扭,为什么,因为很明显,这个aspx页面与权限耦合在一起了,其实,想象一下,这个页面和权限关系有这么大吗?完全没有,去看那些查询UI框,查询方法的实现,都不是,但是为什么就会耦合在一起呢,观念没有转过来。

解耦

很明显,整个页面如果像上面那样写,那就和GetUserRightDepts方法耦合在一起呢,事实上,这个页面最不关心的就是到底是GetUserRightDepts方法,还是其它Get***Depts方法,只要最终都是返回一个List<Dept>结果,那就OK了。

权限与业务不要耦合,因为两者实际上关系不大,有无权限,并不会决定这个程序的执行过程(如上述这段代码)。

那怎么办,很简单,首先转换观念,写这支程序实现的就是查询用户所选部门的考勤数据,没必要我写的查询程序只能查询权限部门,你也可以通过QueryString把能查询的Dept带过来。

而在程序中只想通过一个约定的方法或到一个固定的地方去取就行,如:

 

(List<Dept>)HttpContext.Current.Items["DeptList"],然后再用这个List去绑定部门下拉列表框就完成了。

现在已不需要Hardcode调用GetUserRightList方法了,那这个方法放在哪里呢?在asp.net中很简单,放在一个地方统一处理这类的权限问题即可(如:HttpModule)。

实现一个HttpModule,

这个HttpModule会在程序Request之前的某个事件中就注入某支程序所需要的参数,如

上面这支查询程序的部门列表。

注入方式很简单,可能通过在某个地方配置,如

<Injection Url="query/考勤Query.aspx">

   <Item Name="DeptList" Type="RightDept" />

</Injection>

然后在Module中统一读取这个配置,按照不同的Type去选择不同的注入方式,如

RightDept,就会调用GetUserRightDept方法,然后将结果放到HttpContext.Items["DeptList"]方法中。

一个示例:

    public class SecurityModule : IHttpModule

    {

        public SecurityModule()

        {

        }

        public void Init(HttpApplication application)

        {

            //捕获PreRequest事件

            application.PreRequestHandlerExecute += new EventHandler(application_PreRequestHandlerExecute);

        }

        void application_PreRequestHandlerExecute(object sender, EventArgs e)

        {

            //注入数据权限

            LoginHelper lo = new LoginHelper();

             List<string> deptList = null;

            if(lo.GetUserID()=="admin")

                 deptList = new List<string>(new string[] { "电脑部", "人事部", "财务部" });

            else if(lo.GetUserID()=="zkw")

                 deptList = new List<string>(new string[] { "电脑部"});

             HttpContext.Current.Items["DeptList"] = deptList;

        }

        public void Dispose()

        {

        }

    }

aspx页面示例:

    protected void Page_Load(object sender, EventArgs e)

    {

        if (HttpContext.Current.Items["DeptList"] != null)

        {

            List<string> depts = (List<string>)HttpContext.Current.Items["DeptList"];

            Response.Write("你的权限部门是:<br>");

            foreach (string dept in depts)

            {

                Response.Write(dept + "<br>");

            }

        }

    }

于是数据权限就统一在程序之外,并且可以灵活配置了。

一直以权限设计作为解耦的故事主题,但是最重要的还是一个设计思想,不单是权限的问题,像我们经常在aspx中的Page_Load中写到

string kind = Request.QueryString["Kind"]

这样的代码,它也是耦合,将kind的获取方式强耦合到QueryString上。如果需要通过另一种方式传递kind到这个aspx页面时,一定又要修改代码。

可以在写程序时多问问自己,是否kind一定就会通过QueryString获得,如果不是,那要怎么改?

这些观念其实每个写程序久一点的人或多或少都有,只是每个人的解决方案不同。如果您了解IOC,再对比的想一下吧。

BTW:接触过的很多朋友,在一个新东西出来时,盲目跟风,会按照那个东东做例子,但是一旦到正式的Project时,又按照自己的方式来。

他不明白那个东东替它解决什么问题,因为他根本没有意识到还有那个问题的存在。

结果发现套用框架或使用这个平台时很复杂,还不如按照自己的方式去写,至少比较容易理解。

像WF,AOP,IOC等,知其然,还要知其所以然。

PS:后面这一段是在听到一个同事要在一个签核模块中用WF状态流来设计时有感而发。

本文来自:http://www.cnblogs.com/tsoukw/archive/2008/11/17/1334879.html

分类: 设计模式 系统架构

上一篇:冒号课堂§3.3:切面范式   下一篇:滥用的单例模式有多少害处