Role分析模式(二)角色对象创建和管理

原创 2005年05月28日 02:54:00

概要
Role Object(一)提出对解决Role问题的基本方法,但是我们还有许多问题需要深入,这些问题包括如何创建具体的Role实例,如何管理这些角色并实现角色之间的约束。

Role Object提出了对解决Role问题的基本方法,但是我们还有许多问题需要深入,其中的一个问题是如何创建具体的Role实例。

动机

对Role的创建有几个基本的需求。前面我们使用角色的名字作为Specification来创建Role.譬如我们用字符串”Customer”作为Specification来创建Customer这个具体的Role对象。在大多数情况下,这样做可能就足够了,但是有时候确不行。

假设我们有一个核心的抽象叫做Person。我们知道Person可以是员工,所以有一个叫”Employee”的角色。但是可能有不同类型的员工,如销售人员、开发人员等等。所以一般Employee会建模为一个接口,而由Employee的子类来实现具体的角色。当一个客户应用需要知道一个人的工资时,它从核心去获取一个”Employee”的角色,但是现在把它作为类型的名字显然不适合,因为实际的角色可能具有的名字是”Salesman”,”Developer”等等。

这个关于Specification的问题同样出现在对角色的管理过程,如果单独使用一个类名字不能建立或者获取到一个具体的Role,那么整个管理协议都适应这个Specification进行,而不是单独把类名字当作Key进行管理。

创建过程

对于创建过程来说,我们所要解决的核心问题是需要一个类,当我们向这个类提出某些需求或标准的时候,这个类就会返回满足我们需求的具体对象实例,这种满足可能具有层次关系、或者是其它的关系。

所以,有两个问题,一个是如何表达这个Specification,另一个问题是由谁来做这个中间类,我们是需要重新定义一个,还是使用原来就有的ComponentRole.我们怎样能够这个中间类的使用可以让用户对这个创建过程不可见。

我们对设计模式的认识显然排除了那种使用一个大case的可能性,这样的代码在我们需要动态加入一个角色的时候无能为力。工厂方法看起来适合我们的基本需求,但是你很快就发现我们上面的例子中对任何一个“Employee”,它只能返回一种类型的角色。

幸好,解决方案是存在的,我们有一个模式叫做Product Trader,下面是它的原理图:

在上面的图中,客户负责为每一个具体的产品类建立一个Specification,然后为ProductTrader提供这一

在上面的图中,客户负责为每一个具体的产品类建立一个Specification,然后为ProductTrader提供这一个Specification,Product是一个抽象的接口,它定义了需要实例化的子类的操作。Product Trader这个类则实现了从Specification到一个具体的Creator的映射。并且提供各种操作来管理这个映射关系。这里的映射关系可以是一个非常简单的HashMap或者是一个具有完备功能的中间对象trader.现在如何创建的任务落到了Creator上,Creator知道如何实例化一个具体的产品。Specification是一个表达满足关系的抽象,我们可以看到最简单的例子是一个字符串,可能是一个层次关系或者最复杂的情况下可能是一个需要经过很多计算的公式。它的目的是作为一个关键字搜索到具体的Creator.下面是具体的序列图:

针对我们的Role Object,可以看到ProductTrader的责任由ComponentRole来承担,而每一个具体的Role则是在上图中的ConcreteProduct.而客户实际上是Role Object中的ComponentCore.

为什么使用ProductTrader能够使得我们的角色对象可以被更好地创建呢?

首先,ProductTrader如同其它的创建性设计模式(Factory Method等),可以使得客户独立于具体的产品类。也就是使得ComponentCore独立于具体的Role.应用ProductTrader使得ComponentCore完全独立于具体Role的类层次。ComponentCore只需使用某一特定的Specification调用ComponentRole的createFor方法,由ComponentRole来做具体的创建。

其次,具体的角色类可以在运行时决定。ProductTrader的应用可以让ComponentRole在运行时把某些范围或者条件转化为一个Specification,然后使用这样一个Specification进行具体角色类的搜索。因此,你可以具有可变的运行时才决定的角色创建过程。这个优点带来的最直接的后果就是,你可以进行方便的配置,你可以任意增加、删除特定的角色,只要你通过ComponentRole的方法增加、替换及删除相应的Creator即可。这些Creator可以按照Specification搜索特定的角色类。

我们引入ProductTrader的一个最重要的原因是它可以让你轻松地加入具体角色的类层次。由于ComponentCore成功地和具体角色的类层次、类名字、层次结构和实现策略分离,所以对上述内容的改变不会影响到ComponentCore创建具体角色的接口和方法。

能够很好地配置以及改变具体角色类层次使得加入和删除一个具体的角色类变得什么简单,我们不需要修改任何存在的代码,只要对配置文件或脚本修改即可。


实现的考虑

在角色对象中应用ProductTrader需要考虑四个主要的问题,他们包括: l 如何实现从Specification到Creator的映射

我们需要在一个ProductTrader中维护一个Specification、Creator和它们之间的一个映射关系。

l 如何实现Creator

有很多设计模式可以用来处理这样的麻烦,我们耳熟能详的就有prototype、类对象等等。总而言之,无论用何种方法,你都不会希望每次增加一个新的角色,都需要手工去建立一个Creator。所以,对于Creator的要求就是这个类里面由一种机制可以直接创建一个角色实例.


class Creator {

public Creator(Specification aSpec) {

aSpecification =aSpec;

}

public Specification getSpecification() {

return aSpecification;

}

public ComponentRole create() ;

private Specification aSpec;

}


class ConcreteCreator extends Creator {

private class concreteRoleType;

public ConcreteRole(class concreteRoleType,Specification spec) {

super(spec);

this.concreteRoleType = concreteRoleType;

}

public ComponentRole create() {

return new concreteRoleType.newInstance();

}

}

这个Creator相当简单,注意其实在第一个抽象类Creator中,Specification的实例应当放在ComponentRole抽象类里面用于映射到某个Creator,但是我们处于管理方面的目的,在这里加入了这个成员,你可以在后面看到。

其次,要注意的是,如果使用象C++一样的template,我们可以做到类型检查,但是在这里所有的Specification是抽象的Specification,而具体的Creator生成的是ComponentRole而不是ConcreteRole.

再仔细看一下这个Creator,你会发现create过程没有参数,某些时候,你可能需要其它的创建参数,那么也许你要通过反射得到concreteRoleType这个class中符合你参数的构建方法,然后用你的参数进行创建,而不是直接使用newInstance().当然,前提是在create方法中假如你的参数。

l 如何实现Specification

Specification可以使用某些原语类型来实现,如String代表Class name.当然,复杂的情况需要使用一个自有的对象。如果使用一个专门的接口而并非简单的字符串,Specification的接口看起来如下:


public interface Specification {

public void newFormClient(Object anObject);

public void newFromManager();

public void adaptTo();

public boolean matches(Specification spec);

}

这些接口需要由具体的Specification来实现,其中getRoleClass得到为该Specification对应的Role Class. Matches用于判断两个specification之间的匹配性。NewFromClient则由客户使用。它可以创建一个Specification然后交给ProductTrader. 而adaptTo方法用于把某一个具体的specification和该roleClass相对应,它由ComponentRole使用,ComponentRole通过该方法直接从roleClass中获取到建立Specification所需要的信息。

Specification中newFromManager可以直接缺省实现为adaptTo即可,matches可以实现为相等。我喜欢使用的方法之一先定义接口,然后实现一个abstract类:

public abstract class AbstractSpecification implements {

public void newFormClient(Object anObject);

public void newFromManager() {

adaptTo();

}

public void adaptTo();

public boolean matches(Specification spec) {

getClass().equals(spec.getClass());

}

}

这时候,我们可以实现ComponentRoleSpecification的如下:

class ComponentRoleSpecification extends AbstractSpecification {

private String roleType;

//假设这里的表示方法是一个字符串roleType,你可以使用其它的如roleClass等等

public void newFormClient(Object anObject) {

roleType = (String)anObject;

}

public void newFromManager() {

adaptTo();

}

//注意这里需要ComponentRole有一个静态的getRoleType方法

public void adaptTo() {

roleType = ComponentRole.getRoleType();

}

public boolean matches(Specification spec) {

if (!(super.match(spec))

return false;

if (!(spec instanceOf ComponentRoleSpecification))

return false;

return roleType.equals( (ComponentRoleSpecification)spec.getRoleType());

}

public String getRoleType() {

return roleType;

}

}

最后的问题是ComponentRole如何建立映射,从而把一个Specification映射到一个Creator,这些方法包括addCreator(Creator),removeCreator(Creator),substitue(Creator),我们可以看到前面的Creator中包含了getSpecification这个方法,这是我们可以用如下代码:

addCreator(Creator creator) {

mapping.put(creator.getSpecification(), creator);

}

除了这些维护方法外,还需要有lookup,这个lookup仅仅就是通过一个Specification找到creator的过程,相当简单。

ProductTrader模式的应用可能走得更远,它甚至可以用于整个应用系统的所有对象的创建,对ProductTrader的深入讨论可能会超出Role角色所需要的内容,如果读者需要进一步研究该模式,请参见PLOP3.

Role角色管理

在ComponentRole中,还需要管理那些角色,最重要的操作包括:

hasRole(Spec)

getRole(Spec)

removeRole(Spec)

addRole(Spec)

我们看到,前面的match用于两个Specification之间的精确匹配,理由是我们用它来精确匹配对应的Creator。同时,我们看到,这固然解决了ComponentRole具有多层次的问题,但是却不能提供上面的这些管理接口所需要的所有操作。

回忆一下我们在动机一节提出的问题:

“假设我们有一个核心的抽象叫做Person。我们知道Person可以是员工,所以有一个叫”Employee”的角色。但是可能有不同类型的员工,如销售人员、开发人员等等。所以一般Employee会建模为一个接口,而由Employee的子类来实现具体的角色。当一个客户应用需要知道一个人的工资时,它从核心去获取一个”Employee”的角色,但是现在把它作为类型的名字显然不适合,因为实际的角色可能具有的名字是”Salesman”,”Developer”等等。“

这里的问题是我们并不明确指定一个完全的对等关系,我们需要类层次中一样的概念,如果一个Salesman继承自Employee,那么所有Salesman的对象都是Employee,所以我们需要在Specification接口中加入两个新的操作,分别是:

isSpecialCaseOf(Specification)和isGeneralizationOf(Specification).

这时两个Specification之间可以比较。Specification B是Specification A的一个特殊情况当且仅当对于任何对象X,如果X满足A,那么X就满足B.从我们的角色对象来看,如果它所可能具有的任何一个角色的Specification是用户查询的Specification的一个特殊情况,那么该角色对象就具有用户指定Specification所代表的角色。IsGeneralizationOf则正好相反。

在我们的角色对象中,每次用户通过一个addRole(Spec)加入一个角色对象,ComponentCore在自己维护的一个Map中包存了spec实例到ComponentRole的一个映射,当用户使用hasRole(spec)或getRole(sepc)进行查询时,我们可以遍历该Map,如果发现有某一个spec满足用户的参数,则可以返回。对getRole来讲,可能有两种情况,一种情况是找到一个即返回,另外一种情况则是返回所有满足Specification的角色。


Role和Role之间的约束

我们在一开始就讲到,Role Object很难处理具体Role之间具有约束的问题。这是由Role Object本身的特点所决定的。具体Role 之间没有相互的关联,他们不象Decroator一样一个指向一个,最后指向ComponentCore。

如果Role具体对象之间的关系非常复杂,我们可能需要一个知识层(knowledge level)来处理Role之间的关系。我们在我以后讲到Party[待写]的时候看到,TAO BBC如何使用知识层和Role组合完成一个具有Role的Accountability模式。

其实,我们的Specification也可以看作是一个知识层,对于某些约束,Role可以通过Specification来解决。Specification可以解决具体Role具有一个类层次的问题。那么我们可以递归使用Role Object模式来处理这样的约束。

在实际应用中碰到最多的约束问题可能是,某一个对象的一个角色A可能是该对象能够充当其它角色的一个前提,在银行业务中,如果一个人希望充当贷款人、投资者,那么它首先必须是一个客户。

你可以看到,在上图中我们重复应用了Role Object模式,从而限制了一个Investor角色首先就必须是一个Customer角色,从而形成如下的对象链。

Role的属性列表

Role可能需要动态增加属性,并且处理这些属性之间相互约束,请参见专栏中的文章动态属性(属性列表)

Role的存储

我们在这里没有关心Role的实际存储,我们将Role设计成具体的存储无关,具体的存储方法可能需要另一个包装。譬如使用O/R mapping,或者EJB,我们需要实现对应的RoleEntity层次,然后把RoleEntity实现的行为分派到具体的Role模型。这是我们将在TAO J2EE模式研究中看到的课题。TAO BBC将使用这样的模式来实现具体的存储。


本文使用Product Trader模式和Specification模式讨论了Role的一些具体实现和管理问题。在下一篇文章中,我将描述实现Role的另一种方法。新的方法将采用类Decorator模式来处理Role,这种方式和Role Object各有自己的优缺点。


关于作者
石一楹是一个专注于面向对象领域的系统设计师。目前的工作是国内一家ERP公司的CTO.他住在浙江杭州,有一个两个月的女儿。

使用SPRoleType添加用户权限

在SharePoint 2010和2013中,可以使用SPRoleType方便的向一个list添加用户权限。 例如需要在列表list上,赋予用户User1列表管理员的权限:              ...
  • shrenk
  • shrenk
  • 2013年12月13日 11:52
  • 1440

Jenkins配置基于角色的项目权限管理-Role Strategy Plugin

本文将介绍如何配置jenkins,使其可以支持基于角色的项目权限管理。 由于jenkins默认的权限管理体系不支持用户组或角色的配置,因此需要安装第三发插件来支持角色的配置,本文将使用Role ...

WordPress用户角色及其权限管理编辑插件:User Role Editor汉化版

如果Wordpress默认的用户角色及权限不能满足您的需求,又觉得修改代码编辑用户权限太麻烦。那不妨试试User Role Editor,Wordpress用户角色及其权限管理编辑插件。 User ...

DB2权限管理-组(group) 和角色(role)的区别

DB2权限管理中组和角色的管理的确让很多人(不管是菜鸟还是老鸟)感到困惑。 相对而言,oracle就好多了,根本就没有组的概念,只有角色。 而在DB2和Oralce的概念映射里,DB2的组实际上也被影...

Sql Server 2005中的架构(Schema)、用户(User)、登录(Login)和角色(Role)(二)

在第一节中,我们了解了架构的意义。在第二节的开始,我们暂时忘记架构这个东西。我们假设我们的数据库只有数据库对象。     李老板开了一个小公司,公司有个仓库,堆放了一些货物,由于仓库小,为了节约成本...

建造者模式-Builder Pattern 复杂对象的组装与创建——建造者模式(二):游戏角色设计的建造者模式解决方案

8.3 完整解决方案       Sunny公司开发人员决定使用建造者模式来实现游戏角色的创建,其基本结构如图8-3所示: 北京中美设计培训...

复杂对象的组装与创建——建造者模式(一):游戏角色设计,建造者模式概述

没有人买车会只买一个轮胎或者方向盘,大家买的都是一辆包含轮胎、方向盘和发动机等多个部件的完整汽车。如何将这些部件组装成一辆完整的汽车并返回给用户,这是建造者模式需要解决的问题。建造者模式又称为生成器模...
  • Mark_LQ
  • Mark_LQ
  • 2015年09月12日 20:21
  • 859

分析模式——Role模式详解

分析模式——Role模式详解(角色对象基本概念) 前言:除了gof 23种经典模式之外,实际还很多来自概念业务模型的模式,所谓模式我理解为经验的书面形式,就是人们在设计中所会面对的一些问题的经验总结...

RBAC(Role-Based Access Control)基于角色的访问控制

RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Role分析模式(二)角色对象创建和管理
举报原因:
原因补充:

(最多只允许输入30个字)