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

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

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关联,就可以支持多个层次同时并存的情况。它给出的定义是这样的:

None.gif class  Organization
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif OrganizationStruct[] Parents; 
InBlock.gif OrganizationStruct[] Children;
InBlock.gif dot.gif
ExpandedBlockEnd.gif}

None.gif
None.gif
class  OrganizationStruct
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif Organization Parent;
InBlock.gif Organization Child;
InBlock.gif OrganizationStructType StructType;
InBlock.gif TimeLimit Limit;
ExpandedBlockEnd.gif}

None.gif
ExpandedBlockStart.gifContractedBlock.gif
enum  OrganizationStructType dot.gif {Project2Project, Depart2Project, Company2Project, dot.gif}
None.gif
None.gif
class  TimeLimit
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif DateTime Begin;
InBlock.gif DateTime End;
InBlock.gif 
bool IsFinished;
ExpandedBlockEnd.gif}

None.gif
None.gif


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

 

None.gif class  OrganizationStructTypeEntity
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif OrganizationStructType StructType;
InBlock.gif 
InBlock.gif 
bool Check(Organization parent, Organization child); // 规则
ExpandedBlockEnd.gif
}

 

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

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

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

None.gif class  Party
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif Accountability[] Commissioners;  
// 委托方
InBlock.gif
 Accountability[] Responsibles;  // 责任方
InBlock.gif
 dot.gif
ExpandedBlockEnd.gif}

None.gif
None.gif
class  Accountability
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif Party Commissioner; 
// 委托方
InBlock.gif
 Party Responsible; // 责任方
InBlock.gif
 AccountabilityType StructType;
InBlock.gif TimeLimit Limit;
ExpandedBlockEnd.gif}

None.gif
ExpandedBlockStart.gifContractedBlock.gif
enum  AccountabilityType dot.gif {Project2Project, Depart2Project, Company2Project, Contract, dot.gif}
None.gif
None.gif
class  TimeLimit
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif DateTime Begin;
InBlock.gif DateTime End;
InBlock.gif 
bool IsFinished;
ExpandedBlockEnd.gif}

None.gif
None.gif


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

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

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

知识级的代码描述如下:


 

None.gif class  AccountabilityType
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif PartyType Commissioner; 
// 委托方
InBlock.gif
 PartyType Responsible; // 责任方
ExpandedBlockEnd.gif
}

None.gif
None.gif
class  PartyType
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif AccountabilityType[] Commissioners;  
// 委托方
InBlock.gif
 AccountabilityType[] Responsibles;  // 责任方
ExpandedBlockEnd.gif
}

None.gif
None.gif操作级的代码描述如下:
None.gif
None.gif
class  Accountability
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif AccountabilityType Type;
InBlock.gif Party Commissioner; 
// 委托方
InBlock.gif
 Party Responsible; // 责任方
InBlock.gif
 TimeLimit Limit;
InBlock.gif SomeAction[] Actions;
ExpandedBlockEnd.gif}

None.gif
None.gif
class  Party
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif PartyType Type;
InBlock.gif Accountability[] Commissioners;  
// 委托方
InBlock.gif
 Accountability[] Responsibles;  // 责任方
ExpandedBlockEnd.gif
}

None.gif
None.gif


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

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

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

 

None.gif class  PartyType
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif PartyType[] BaseTypes;
InBlock.gif AccountabilityType[] Commissioners;  
// 委托方
InBlock.gif
 AccountabilityType[] Responsibles;  // 责任方
ExpandedBlockEnd.gif
}

 

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

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

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


 

None.gif class  AbstractAccountabilityType
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif PartyType Commissioner; 
// 委托方
InBlock.gif
 PartyType Responsible; // 责任方
ExpandedBlockEnd.gif
}

None.gif
None.gif
class  PartyType
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif PartyType[] BaseTypes;
InBlock.gif AccountabilityType[] Commissioners;  
// 委托方
InBlock.gif
 AccountabilityType[] Responsibles;  // 责任方
ExpandedBlockEnd.gif
}

None.gif
None.gif
class  Accountability
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif AccountabilityType Type;
InBlock.gif Party Commissioner; 
// 委托方
InBlock.gif
 Party Responsible; // 责任方
InBlock.gif
 TimeLimit Limit;
InBlock.gif SomeAction[] Actions;
ExpandedBlockEnd.gif}

None.gif
None.gif
class  Party
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif PartyType Type;
InBlock.gif Accountability[] Commissioners;  
// 委托方
InBlock.gif
 Accountability[] Responsibles;  // 责任方
ExpandedBlockEnd.gif
}

None.gif
None.gif
class  LevelAccountabilityType  //  分层的责任类型
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif PartyType Commissioner; 
// 委托方
InBlock.gif
 PartyType Responsible; // 责任方
ExpandedBlockEnd.gif
}

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

InBlock.gif PartyType Commissioner; 
// 委托方
InBlock.gif
 PartyType Responsible; // 责任方
ExpandedBlockEnd.gif
}

None.gif
None.gif


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

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值