适配器模式:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
书本上给出了一个很好的例子:NBA里面,姚明是一个外籍中锋,他和其他成员的语言不通,那么他就需要翻译者来“适配”他,最终使得他能够很其他的成员一起打球(这里讲到的主要是对象适配器)
模式中的成员:
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
需要适配的类(Adaptee):需要适配的类或适配者类。
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
例子中的成员:
球员—Target:具有同一种语言的球员
翻译者-Adapter:将外籍中锋的语言进行翻译,使得外籍中锋能够理解球员类的语言
外籍中锋-Adaptee:和球员们的语言不同,需要翻译者
图中可以看出每个球员都可以接受教练的两个命令,但是外籍中锋能够理解的语言和其他的球员不同,所以需要翻译者的翻译
代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 适配器模式 { //球员的抽象类 //包括球员的名字 进攻方法 防守方法 public abstract class Player { //名字属性 //设置的name修饰符是protected 在抽象类及其子类中可以访问 //如果设置成为private那么在任何其他的类中都不可以访问 /******************** * 这里遇到了两种情况 * 一种就是下面这种情况--变量设置成为protected类型,通过构造函数来设置变量 * 另外一种是: 变量设置为private 那么就要通过属性的方法来设置变量 *********************/ protected string name; public Player(string name) { this.name = name; } //在子类中重写 //进攻方法 public abstract void Attack(); //防守方法 public abstract void Defense(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 适配器模式 { //翻译者继承了球员 //需要将教练的命令翻译成外籍中锋能够听懂的语言 //在客户端翻译者是直接和外籍交流的人 class Translator:Player { //如何进行翻译工作 //首先:实例化一个外籍中锋对象 ForeignCenter wjzf = new ForeignCenter(); //然后:初始化外籍中锋的名字 public Translator(string name) : base(name) { //通过翻译者给外籍中锋设置名字(注意:没有初始化翻译者的名字) wjzf.Name = name; //省略代码:this.name = name; } //最后:进行翻译工作--重写球员的方法 //如何实现:调用外籍中锋的方法--也就是说翻译者将attack和defense翻译给了外籍球员;使得外籍球员在听到attack和defense知道调用哪种方法 public override void Attack() { wjzf.进攻(); } public override void Defense() { wjzf.防守(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 适配器模式 { //外籍中锋(有语言上的差异) //并不能懂得attack defense命令 //只能懂得 进攻 防守命令 class ForeignCenter { //设置名字 private string name; public string Name { get { return name; } set { name = value; } } //进攻和防守方法- //和前面的不同:方法的名字是中文 public void 进攻() { Console.WriteLine("外籍中锋 {0} 进攻",name); } public void 防守() { Console.WriteLine("外籍中锋 {0} 防守",name); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 适配器模式 { //前锋 class Forwards:Player { //构造函数继承父类 public Forwards(string name) : base(name) { } //在子类中重写进攻和防守两个方法 public override void Attack() { Console.WriteLine("前锋 {0}进攻",name); } public override void Defense() { Console.WriteLine("前锋 {0}防守",name); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 适配器模式 { //后卫 class Guards:Player { //构造函数继承父类 public Guards(string name) : base(name) { } //在子类中重写进攻和防守两个方法 public override void Attack() { Console.WriteLine("后卫 {0}进攻",name); } public override void Defense() { Console.WriteLine("后卫 {0}防守",name); } } }客户端代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 适配器模式 { class Program { static void Main(string[] args) { Player xm = new Forwards("小明"); Player xmm = new Center("小明明"); xm.Attack(); xmm.Defense(); //翻译者完全替代了外籍中锋--翻译者直接和教练交流 //实例化姚明这个外籍中锋利用的是翻译者类 Player ym = new Translator("姚明"); ym.Attack(); ym.Defense(); Console.Read(); } } }执行结果:
基本上搞过it的都知道权限控制其实是很麻烦的一件事情,但是不难理解。在我学习rbac之前,对权限的理解基本上就是权限分配给角色,角色又分配给用户组,然后用户可以属于用户组之类的。一些企业级应用可能会有更复杂的情况,比如A部门的员工甲,就分配A角色给甲;若甲在B部门兼职,那就不是简单的把B角色分配给甲,兼职还是有区别的;rbac也只是一种模式而已,看下面的标准介绍:
NIST(The National Institute of Standards and Technology,美国国家标准与技术研究院,2004)标准RBAC模型由4个部件模型组成,这4个部件模型分别是:
·基本模型RBAC0(Core RBAC);
·角色分级模型RBAC1(Hierarchical RBAC,含General,Limited);
·角色限制模型RBAC2(Constraining RBAC,含Static/Dynamic Separation of Duty,即SSD和DSD);
·统一模型RBAC3(Combines RBAC)。
先从rbac0开始,上图:
此图从右往左分析
1、先看ob对象,也就是我们需要操作的对象,可以是用户本身,可以是角色本身,也可以是任何需要做权限控制的实体或虚拟的数据;
2、然后是op操作,操作不是权限,操作 + 对象 = 权限,应该这样理解
拿windows的文件来举例,那么ob就是文件,op就是 读、写、执行等,而权限 可以理解成 ob 和 op的笛卡尔乘积;例如 对文件的读权限、对文件的写权限等
3、prms权限,就是图中的大红圈,这个就不用解释了;role角色、user用户 这2个也很好理解
4、session这个比较难理解,现在我们假设没有session这个环节,其实权限控制也基本成型;
从分配上理解,prms可以分配给多个role,role可以有多个prms,role可以分配给多个user,user可以有多个role;
这样涉及到一个问题,当用户登录系统后,它具有的权限怎么计算呢?当然是先找这个用户有多少个角色,然后通过角色找总共有多少个权限,这样得到的一个权限的集合,这个集合就代表这个用户登录系统后的权限;
但是有的系统不是这么考虑的,rbac也不是这么考虑的;例如oracle,使用过的人应该知道,我们用sqlplus登录oracle时,需要选择此次登录,是用sysdba还是normal,这其实是对角色的一种选择;如果你选择以normal方式登录,那sysdba部分的权限就没有;那是不是sysdba部分的权限没有分配给用户呢?其实也不是
所以可以这样理解,所有分配给用户的权限是一个大的集合,而且是处于未激活状态,每次用户登录系统的时候,有选择的激活一部分的权限,作为此次登录系统的权限集合;
那如何做到这点,看rbac0的图,就是session;session是关联用户与角色的,每次用户登录,就会激活一个session,这个session对应的角色下的权限,就是此次用户登录系统的权限集合;
我个人理解,对于一些小的系统,比如一个简单的新闻发布网站,想用rbac来做权限控制,或者是自己想写一个简单的rbac小框架,可以考虑去除session的rbac0模式;
然后要注意ob对象这个数据库存储的设计,例如ob代表系统中注册的用户,有2000人,从理论上讲,用户的操作有 读、修改、删除、新增,如果要分配的话,那所有用户都对自己的个人信息有 读、修改 权限,那就有 2000对象 x 读、修改操作 = 4000个权限,然后分配给2000个角色,这2000个角色又单独分配给对应的2000个用户,这样就变的非常的麻烦;
而且,管理员有所有用户的 读、修改 操作权限,那每新增一个用户,都要把这个用户 维护到 ob中,再产生prms,再分配给管理员,这样也非常的麻烦;
如果哪个系统需要这么完整细微的权限控制,那麻烦是必须的,如果简单的系统,例如用户只有 普通用户 和 管理员 2种,那完全没有必要弄的那么复杂;
我个人理解,rbac模式只是给出一个思路,具体实现是可以灵活多变的;
比如ob,我可以把“所有用户”作为一个ob,然后 对此ob的操作权限分配给管理员,这样就不怕动态新增用户的时候,需要维护权限表;
再如,我加上潜规则,在用户查看自己的信息、或修改自己的信息时,可以不经过rbac的权限校验,直接查看;当然,你要理解,这个潜规则,就不是rbac层面的东西了,应该是业务层面的东西;
再举个例子,用户新增单据的权限,限制用户只能最多新增10张单据,那这种权限在rbac中如何配置如何体现?
好吧,我只能告诉你,没有办法。rbac做的仅仅是 有 或者 没有 这个权限;至于 什么情况下有,是次数限制 还是 时间段限制,这些都不是rbac关心的,这些是业务层逻辑关心的。
好吧,说多了,来看看rbac1,上图:
rbac0 和 rbac1之前的差别在rh角色继承,这个继承和oop中类的继承很相似;不多解释,看看官方点的说明
角色间的继承关系可分为一般(General)继承关系和受限(Limited