关于DCI的两个疑问

大家知道DCI中有场景、角色、角色扮演者、角色扮演者的属性和方法、场景相关的属性和行为这些概念。
当一个领域对象扮演某个角色参与到场景中时,它同时也被注入了一些和该场景相关的某些属性和行为,这些属性和行为我们通常称为场景属性和场景行为。我的三个问题是:
1)场景属性和场景行为是属于角色的还是属于领域对象的?
2)参与场景的是角色,那么我们在场景中的其他角色能访问某个角色的扮演者吗?
3)像javascript这样的动态语言可以简单实现动态注入行为或属性,但是我觉得javascript也不太好,因为类型不安全,我必须在运行时才能知道某个对象是否具有某个属性或行为;但是想C#或java这样的静态语言又无法实现动态注入,我想到采用对象组合的方式我个人觉得也很合适?比如我定义一个角色类,如Reader,他由一个Student实体扮演,Reader的构造函数中接受一个Student实例,通过这样的方式我觉得也很自然,并且也能达到动态给领域对象增加行为或状态的目的;其实场景关心的是角色,至于这个角色是一个被动态注入了行为的领域对象还是一个真正的角色类它其实不关心。这其中我发现一个根本矛盾并且是无法解决的矛盾就是:如果是动态语言,虽然实现动态注入属性和行为,但不是强类型,就会有很多非强类型的语言的缺点;但是如果是静态语言,则无法实现动态注入属性和行为,因为一个类型的接口是明确的;

面对这些问题,希望大家能给我一些指点。谢谢。
[该贴被tangxuehua于2011-06-25 17:00修改过]

发现双休日这个论坛不活跃啊,哈哈。

我先自己再发表一些看法吧。

1)我认为场景属性和场景行为都是属于角色的;原因是,大家都知道所谓的场景行为必须是某个实体扮演某个角色后才会具有的,一旦该实体不具有那个角色了,则该实体也就不会再有那个场景行为了,而场景属性是场景行为的产物,自然也应该和场景行为同生同灭。比如刘国梁以前是运动员,而现在是教练。运动员和教练是两个角色,如果我们把运动员和教练这两个角色相关的所有的属性都放在刘国梁这个实体中,我觉得显然不合理。当我们把刘国梁这个实体转换为教练这个身份即角色时,才会去关心他的一些和教练这个角色相关的属性。所以,运动员的相关属性以及教练的相关属性都应该是属于其相应的角色的;

2)假设有一个场景,有三个角色(A,B,C)参与,A,B,C三个角色之间可能会有一些交互。那么我们在这个场景过程中,A角色能否知道B角色的实体扮演者是谁呢?我认为不可以,因为:如果A角色知道了B角色的实体扮演者的话,那就意味着A角色可以直接和B角色的实体扮演者打交道,也就是访问B角色的实体扮演者的一些属性或行为。而实际上这种访问是被禁止的。比如银行转帐的例子,有两个角色SourceAccountRole, TargetAccountRole, 这两个角色都由某个银行帐号(BankAccount)实体扮演,SourceAccountRole只提供了扣除钱的功能,TargetAccountRole只提供了增加钱的功能,但是他们的扮演者BankAccount则提供了存钱(Deposit)、取钱(Withdraw)、查看余额(Balance)等功能;所以,假如SourceAccountRole能够访问TargetAccountRole的角色扮演者即BankAccount实体的话,则它就可以直接调用该BankAccount实体的Withdraw方法,那起不是乱套了,这样就无法确保只允许场景中能发生的行为发生了,因为还会发生不该发生的事情。

3)我现在认为其实我们也可以把角色看成是一种类型, 而一个具体场景中的参与活动的那些活动参与者就是这种角色类型的实例。大家都知道Entity是一个类型的实例,而我们知道Entity和角色实例其实在运行时其实是同一个东西,因为它们具有相同的ID(唯一标识),角色实例就是由一个Entity扮演的。对于场景而言,他不知道这件事情,他不知道Entity的存在,它面对的只是角色。所以基于以上的认识,我认为一个Entity在参与到场景之前和之后,它的ID没有变化,但是它的身份变化了,而实际上它还是同一个客观存在,因为只是身份变化了。所以我可以放心的把这个身份(即角色)用类型来表示,即当这个Entity在参与到场景之前是ClassA,然后在参与到场景后,其类型就表现为ClassB;ClassA是Entity的本来类型,而ClassB则是Entity参与到场景后所表现出来的身份(即角色)的类型。我认为我们在设计时完全可以用组合的思路,通过装饰者模式将同一个Entity的身份改变为ClassB(我们要做的仅仅是在ClassB的构造函数中接受ClassA的一个实例即可),然后让他参与到实际场景中去。而我前面所说的哪些场景属性和场景方法则都可以放在ClassB中,另外我们也可以像普通对象一样把这个ClassB的实例持久化到数据库,当然具体持久化时,我们可以把ClassB的所有属性(场景属性)也持久化到和ClassA对应的表中,因为毕竟是同一个东西嘛,或者当然也可以分开保存。我们要确保的只是在被重新取出来时将相应的属性能够分开(ClassA的属性归ClassA,ClassB的属性归ClassB)即可。通过这样的做法,我觉得不仅可以很优雅的解决Entity和角色之间的属性及行为的拆分,并且还能很好的解决实体属性和场景属性的持久化和重建的问题。通过这样的思路,我甚至认为我们根本不需要采用什么动态注入行为的支持就可以很好并且也有一定理论基础的实现DCI架构。大家觉得如何呢?

4)最后,我还有一点想不太明白的,我目前还找不到规律,是否任何场景都要搞个角色,因为我觉得有些场景似乎不用角色的参与就能完成,定义了角色反而会感觉有点过度设计。比如存钱或取钱,我认为这两个场景用一个银行帐号实体就可以完成,这是一个银行帐号的基本功能,无需任何角色就可以完成。所以这种情况难道我也必须要搞出相应的角色去做这两个行为吗?我现在的思路是,如果一个场景,当他所要求参与者所具有的行为是该参与者本身就拥有的基本核心行为,也就是说不扮演任何角色就已经具有的行为,则我觉得可以直接让实体,即PPT参与到场景中;而如果该行为是依赖于场景的才会有的,那么Entity就应该扮演某个角色后再参与到场景中来。但问题是,我们有时候很难判断出该行为是属于Entity本身该具有的行为还是场景行为?这点我思考了很久还是没有摸索出规律来,希望能多多得到大家的指点。

banq,我写了这么多字,用心良苦啊。另外说个题外话,我是搞.NET的,但是我很喜欢思考,并不是说搞.NET的人就不会思考。技术是死的,人是活的,我认为人只要有思想,会独立思考,即便在相对沉闷和封闭的平台环境中,照样也能不断突破自我。
[该贴被tangxuehua于2011-06-25 23:19修改过]

一般认为,四色原型(MI/ROLE/PPT/DESC)属于分析方法,DCI(DATA/Context/Interactive)为设计方法,但他们背后的想法可以说是相通的,甚至可以说一致的,不妨放在一块说。MI和Context表示具体业务的活动或场景,PPT和DATA表示参与该业务活动或场景的实体或数据模型,这些概念比较清晰,不必多语。

现在需要两个概念在它们之间搭建起桥梁:role和Interactive。先说说为什么需要它们?因为业务活动或场景,业务实体或模型,都具备多样性,如果不考虑多样性,这两个概念的引入就没有必要,同样,在单一的背景下,也不好觉察这些概念的价值。

如果我们将所有行为都放在业务活动中,这就是面向过程的做法;如果我们将所有行为放在业务实体中,这就是面向对象(类)的做法。之前,讨论过一些关于这两种做法的极端性,我认为前者是忽略共性,不做任何必要的抽象的做法,而后者则是过度抽象,忽略了个性。算得上是一种“过犹不及”的现象。

Interactive和Role分别从“因”和“果”两个角度描述“场景”与“实体”之间的桥梁。interactive描述实体在场景中的交互过程或动机,role实际上描述实体交互后的结果,即发挥了什么的作用,扮演了怎么样的角色。Role虽然翻译为角色,但角色的本意是“发挥作用”,表示实体在交互中发挥了怎么样的作用。

Role是在特定场景下,对诸多实体行为的一种抽象,是一种依赖场景的抽象,也就是说其具备了场景的个性与实体的共性。场景本身是对实体的抽象过程的一种约束,不像原来的class-oriented,没有考虑这种约束。可以说Role是有约束的class,试图让class与object和谐共处。

现在我尝试给出我的答案,仅作参考。
1)场景的属性和行为是自己的,既不属于角色也不属于领域对象。角色的行为是在“场景”的约束下对“领域对象”行为的一种抽象,即对“领域对象”依赖场景的共性行为的提取。场景的属性和行为是描述场景的业务规则,是对角色行为的控制或制约。

2)在场景中实体不仅可以发挥依赖场景的角色作用,也可以发挥不依赖具体场景的作用,这些不依赖于场景的作用就应该放在扮演者身上。

3)角色是一种有场景约束的行为抽象,但这也意味着其要具备稳定性,可以静态表达;但在我们无法很好的抽象,也就是对角色的定义并不稳定、明确时,使用动态表达更自然。想想生活和自然的情景,你会发现这个不难理解。

稍微总结一下:

场景的行为和属性 = 描述业务的规则和属性,制约交互行为的规则
角色的行为和属性 = 依赖场景的实体的抽象,实体的交互行为
模型的行为和属性 = 不依赖场景的实体的抽象,实体的个体行为

角色的行为和属性如果有明显的稳定的概念特征,用静态、显式的方式表达,如果没有,则用动态、隐式的方式表达。银行账号的例子,可以将“源帐号”和“目标帐号”以“角色”的概念显式地表达出来,但我觉得,这里用两个表达其意思的变量名来表示,在过程的参数中有所区别就可以了。

场景与实体的行为,比较容易区分。实体的角色行为与模型行为的区别,取决对场景的依赖。要区分也不难,只要这些行为基本属于“自娱自乐”的就是模型行为,也就是说这些行为使用的数据不是通过与别的实体交互得来,与外界相对独立,就划分为模型行为。如果行为使用的数据是通过与别的实体交互得来,则可划分为角色行为。

关于Role,我想再说一点,世上本没有Role,发挥相同作用的PPT多了,便有了Role,从此便有了扮演Role的这种说法了。

其实无论DCI、MVC、四色原型、DDD无非让代码更合理、更自然地表达现实的问题与解决方案,也就把代码井然有序地分开放在该放的地方,找起来方便,理解起来方便,改起来方便,有效地降低并控制复杂度,不会乱成一锅粥,此外,还有别的吗?

DCI其实是为Scala准备的,你看一下Scala这个DCI案例,可能觉得就比较简单,另外DCI侧重行为,那么侧重类关系的结构问题它就解决不了(如同设计模式分结构型模式和行为型模式一样,侧重点不同),反而使用Java/C等比较合适,器有所长也有短。

2011年06月25日 22:58 "@tangxuehua"的内容
我们有时候很难判断出该行为是属于Entity本身该具有的行为还是场景行为? ...

这就是问题关键,这需要使用对象责任职责协作

不好意思,暂时不能继续和大家提问和交流,因为这两天实在太忙了。没办法静下心来专心思考。感谢banq和jdon007,你们能回复我的帖子我很感激。