分析模式读书心得之责任模式

分析模式读书心得之责任模式

Martin Fowler的《分析模式》买了许久,也看了很多遍,始终未能全部领会。这篇读书心得也只是疏浅的一点看法而已。

要说责任模式(Accountability)还需要先从组织架构的程序实现谈起。凡是一个项目,或多或少都要涉及到部门组织架构的描述。目前我们的解决方法无非就是两种:一种是需要递归的处理方式,另外是一种不需要递归的处理方式。

所谓需要递归的处理方式,就是每一个部门采用如下的数据结构存储:depart(dept_id,parent_id, ...)。这样的优点是可以表示无限的级别,缺点是处理速度慢,大部分都要用到递归。
所谓不需要递归的处理方式,就是每一个部门采用如下的数据结构存储:depart(levelcode, ...)。这里的levelcode是一种具有层次关系的数据结构,比如字符串方式:company.software.development.java_team,或者类似邮政编码的方式0105080905。这样的优点是处理速度快,直观,通常不需要递归,缺点是表达受到存储容量的限制(比如字段长度等等)。

为了表达的需要,我们以下例子都采用需要递归的结构来表达。

现在就提出问题了,很多时候部门结构并不是单一的,参考常用的矩阵式项目组织结构,一个项目组既需要向公司的项目管理机构负责,也需要向所在的部门负责,这种结构我们如何来表示?

一般来说,如果世界只是这么简单的话,我们毫无疑问可以把刚才的部门定义修改成这样:project(proj_id, dept_id, proj_mgr_id, ...)。事实上程序分析设计人员要面临的世界很复杂,如果需求中公司的CEO准备直属领导该项目的时候怎么办?所以我们需要寻找一种更有效和更简单的表示方法,来适应这种多个层次结构体系同时并存的情况。

《分析模式》把部门、项目、公司甚至于个人都笼统地概括为“组织(Organization)”。通过使用一个中间对象(表)替代我们上述的直接id关联,就可以支持多个层次同时并存的情况。它给出的定义是这样的:

class Organization
{
 OrganizationStruct[] Parents; 
 OrganizationStruct[] Children;
 
}


class OrganizationStruct
{
 Organization Parent;
 Organization Child;
 OrganizationStructType StructType;
 TimeLimit Limit;
}


enum OrganizationStructType{Project2Project, Depart2Project, Company2Project, }

class TimeLimit
{
 DateTime Begin;
 DateTime End;
 
bool IsFinished;
}



但是这样的结构虽然灵活性够高,但是约束性不够。因为用以上的结构,我们同样可以把“项目”描述为“公司”的上级部门了。那么如何增加这种结构的约束性呢?很简单,把OrganizationStructType从一个简单的enum定义成一个实体就可以了:

 

class OrganizationStructTypeEntity
{
 OrganizationStructType StructType;
 
 
bool Check(Organization parent, Organization child); // 规则
}

 

然后用OrganizationStructTypeEntity替换OrganizationStruct中的OrganizationStructType就可以了。当添加一个新的关系的时候,我们可以用Check来验证一下,确保数据合乎正常的逻辑。当然,这里给出的代码并不是最优化的,仅仅是为了说明一个概念而已。

《分析模式》中的“责任类型”并不光是用来解决组织架构组织的问题。现实中普遍存在有各种层次性的关系,包括各种组织架构、合同等等。不过《分析模式》中定义“责任类型”的应用范畴是“可变的”关系,这也是为什么我们需要有一个 TimeLimit 存在的原因。对于描述“爸爸”和“儿子”的数据结构关系的时候,我们就需要用开始的id直接关联的方式;但是如果描述的是“监护人”和“被监护人”的概念,我们就可以应用“责任类型”的模式了。

当我们把“组织结构”关系抽象升华到“责任类型”的层次的时候,我们需要一组专门的名次来替代以前的提法,所以我们把以上的代码修改一下:

class Party
{
 Accountability[] Commissioners;  
// 委托方
 Accountability[] Responsibles;  // 责任方
 
}


class Accountability
{
 Party Commissioner; 
// 委托方
 Party Responsible; // 责任方
 AccountabilityType StructType;
 TimeLimit Limit;
}


enum AccountabilityType{Project2Project, Depart2Project, Company2Project, Contract, }

class TimeLimit
{
 DateTime Begin;
 DateTime End;
 
bool IsFinished;
}



责任类型模式的应用面当然比单纯的“组织结构”要广泛的多。更重要的是,我们程序的复杂度并没有增加。这里摘抄书本中的两个例子:
John Smith为某公司工作,这里可以被建模为一个责任类型,其中某公司是委托方,John Smith是责任方,责任类型是雇佣关系。
John Smith允许Mark医生作内窥镜检查,这里可以被建模为一个患者许可类型的责任模型,其中Mark医生对John Smith负责。

但是,责任依然给模型带来了复杂性,因为责任类型显然要比组织结构类型要多得多。这个将带来我们应用上的麻烦:我们要为新的责任类型编制规则--每个责任类型都要一个规则!如果这个要放到数据库中去,可就麻烦了。

所以我们还需要改进这个模式,就是要把这些约束相关的数据抽取出来,和具体的算法独立开来。《分析模式》告诫我们一个建模的原则:“把模型清晰地分解成操作级和知识级”。这里的“知识级”很多人也称之为“元模型”,但是Martin说明了他不用“元模型”来称呼“知识级”的原因。

知识级的代码描述如下:


 

class AccountabilityType
{
 PartyType Commissioner; 
// 委托方
 PartyType Responsible; // 责任方
}


class PartyType
{
 AccountabilityType[] Commissioners;  
// 委托方
 AccountabilityType[] Responsibles;  // 责任方
}


操作级的代码描述如下:

class Accountability
{
 AccountabilityType Type;
 Party Commissioner; 
// 委托方
 Party Responsible; // 责任方
 TimeLimit Limit;
 SomeAction[] Actions;
}


class Party
{
 PartyType Type;
 Accountability[] Commissioners;  
// 委托方
 Accountability[] Responsibles;  // 责任方
}



一旦有了知识级的描述之后,我们很容易用外部的代码来验证其中的约束关系:
 所有的 Party.Comissioners 必须隶属于 PartyType.Comissioners。
 所有的 Party.Responsibles 必须隶属于 PartyType.Responsibles。

有了“知识级”的概念之后,我们的“责任类型”又可以保存到数据库中去了。当然,做这样的划分目的并不只是为了能够放到数据库中哦。

我们还继续要对“责任模式”进行改进,因为我们遇到了新问题:小王是一个项目经理,同时因为常见的原因又兼任项目的系统分析员。使用以上的模型,我们可以把小王看作是项目经理,也可以看作是系统分析员,但是不能两者都是。当然我们有很多的折衷办法来处理面前的这种情况,也可以采用《分析模式》给我们的解决方法:让团体类型可以相互继承。这样我们就可以创建一个“项目经理兼分析员”的团体类型了。修改之后的代码如下:

 

class PartyType
{
 PartyType[] BaseTypes;
 AccountabilityType[] Commissioners;  
// 委托方
 AccountabilityType[] Responsibles;  // 责任方
}

 

回过头来我们再来评估一下:“总公司、区域分公司、分公司、销售办事处”这样的序列是否可以用“责任模式”来描述。因为“责任模式”和直接id关联的方式相比,就是放宽了约束,现在我们通过以上步骤,把约束能力大大加强了,做这个评估,就是要看一下我们的模式是否能够很好地兼容“老方法”,这个也是系统设计过程中常用的一种验证方法。

我们需要定义“区域分公司责任类型”来对“总公司”负有“区域管理”的责任,定义“分公司责任类型”对“区域分公司”负有“管理分公司”的责任,定义“分公司责任类型2”对“总公司”负有“管理分公司”的责任,定义“销售办事处责任类型”对“分公司”负有“管理销售办事处”的责任,定义“销售办事处责任类型2”...,定义“销售办事处责任类型3”...。不过要这样来定义确实太笨拙了,有没有更好的改进方法呢?

当然有!我们可以扩展出一种新的“分级的责任类型”,用它定义一个“公司层次结构”的责任类型,然后该责任类型中的每一级都跟一个团体类型的序列“总公司、区域分公司、分公司、销售办事处”中的对应类型相关联。《分析模式》把这种责任类型叫做“分级的责任类型”,与此对应,我们先前讨论的责任类型就叫做“分层的责任类型”。《分析模式》中认为两者的区别在于:“分层的责任类型扑火团体的责任关系并组成一个层次性的模型”,也就是说“团体只能负有一种该类型的责任”;而“分级的责任类型”则是用于捕获其中具有固定次序的团队责任关系”。改良之后的代码如下:


 

class AbstractAccountabilityType
{
 PartyType Commissioner; 
// 委托方
 PartyType Responsible; // 责任方
}


class PartyType
{
 PartyType[] BaseTypes;
 AccountabilityType[] Commissioners;  
// 委托方
 AccountabilityType[] Responsibles;  // 责任方
}


class Accountability
{
 AccountabilityType Type;
 Party Commissioner; 
// 委托方
 Party Responsible; // 责任方
 TimeLimit Limit;
 SomeAction[] Actions;
}


class Party
{
 PartyType Type;
 Accountability[] Commissioners;  
// 委托方
 Accountability[] Responsibles;  // 责任方
}


class LevelAccountabilityType // 分层的责任类型
{
 PartyType Commissioner; 
// 委托方
 PartyType Responsible; // 责任方
}


class HierarchicalAccountabilityType
{
 
string Level; // 例如:01020304或aa.bb.cc.dd.ee 表示层次位置,不过这里只需要用1,2,3,4的单数序号了,更简单
 PartyType[] Lists; // 团体类型序列

 PartyType Commissioner; 
// 委托方
 PartyType Responsible; // 责任方
}



这里没有描述书本中图2.13“反复权衡责任类型的子类型”,因为看书看来看去,没有找到什么“定向的责任类型”的说明,而且四处找不到可用的英文版的文档下载,谁可以提供一下,万分感谢。

最后,在责任模式的操作范围定义中,提出了责任模式可以引入“职位”的概念,所有的职责可以和职位关联,然后个人再和职位做一个“雇佣”的责任模型,不过书中建议除非职位变化很快,否则就不要再引入太多了。

 

posted on 2005-07-11 00:02  老翅寒暑  阅读(4647)  评论(5编辑  收藏  举报

导航