呵呵,今天接着写。昨天晚上只写了一部分于时间处理有关系的类,下面我们来看看重构之前和重构之后,以及整个重构过程的思考。
首先我们来看一下没有采用场景的方式,以下是DiggStorySynchronizedEventProcessor处理器的实现方式,它是一个同步的事件处理器,异步的类似。
// 以下是DiggStorySynchronizedEventProcessor的实现: public class DiggStorySynchronizedEventProcessor implements DomainEventProcessor{ private DiggStoryEventRepository diggStoryEventRepository; private FeedRepository feedRepository; public void processEvent(DomainEvent domainEvent){ DiggStoryEvent diggStoryEvent = (DiggStoryEvent)domainEvent; //存储DiggStoryEvent以便以后统计哪些人顶了Story this.diggStoryEventRepository.add(diggStoryEvent); //新建一个Feed,表示某个人顶了某个Story, Feed feed = new Feed(); Property property = new Property(); property.setName("title"); property.setValue("Digg the story"+diggStoryEvent.getStory().getName()); //....动态的增加新的Property feed.add(property); //保存Feed,以便让好友可以看到自己的动态 this.feedRepository.add(feed);
//以后也许会有新的行为加入 } }
|
好了,有了DiggStorySynchronizedEventProcessor ,我们再看看Story如何与其交互,以下是Story的实现:
// Story实现: public class Story implements Diggable { public void digg(EventHandler eventHandler,User user){
DiggStoryEvent diggStoryEvent = new DiggStoryEvent(this,user); diggStoryEvent.accept(eventHandler); }
}
|
好了,以上就是没有重构之前的代码,我们分析一下这样做会带来什么不好的后果,我们知道事件处理器本身是技术的组件,以后是可以替换的,而这里我们在事件处理器里写了很多的代码,也许以后还有进行其它的行为的拓展(这包括保存事件本身,保存Feed等等),这样以来,表面上好像是领域模型+领域事件了,好像是面向对象了,其实还不够彻底。技术组件不应该实现逻辑的,业务逻辑应该由模型来实现,技术组件仅仅是调用模型的逻辑而已,那么这里的逻辑到底放到什么模型里面呢?如果没有引入场景的情况下,我们也许会想到领域服务,但是如果真这样,我们又会发现以后,领域服务的代码将变的不好维护,因此这个时候就是需要引入场景的地方了,因为具体的场景对象具有当前顶这个实际场景的语义,这也是与场景所符合的。
OK,知道场景的概念以后,我们又会遇到一个问题,那么就是场景对象的生命周期,因为万事万物都有生命周期,场景也一样,它也得生,也得死,因此,我们就来考虑一下场景的生命周期,起初重构的时候,我引入了EventContext的概念,一个事件发生的时候对应相应的事件上下文,这样我就将上面的DiggStorySynchronizedEventProcessor的代码移到了EventContext里实现,这样实现是实现了,但是我后来发现,EventContext这个纯虚构(GRASP模式之一)的模型是否真正的合适,因为采用EventContext实现以后,我发现上下文对象的生命周期和Event的生命周期是不一致的,因为有时候场景的行为也会触发事件,场景和事件是不能强耦合的,同时因为事件需要在多个分布式节点之间传递,因此我们需要保证事件的“容量”,它不能太大,这里的太大包括属性和行为等。因此我后来废掉了EventContext,引入了DiggStoryContext对象,DiggStoryContext对象负责在顶贴这个行为发生的时候,完成相应的逻辑处理,同时事件和场景还有个先后顺序的关系,首先场景是由用户触发的,而事件是由模型触发的,用户首先顶一帖子,业务层会创建一个DiggStoryContext对象,让后Story以被顶者的角色加入到当前的场景,当Story加入到顶贴这个场景中之后,Story触发DiggStoryEvent事件。
呵呵,经过上面的分析,我进一步进行了重构,最终重构后的代码如下:
首先来看看DiggStoryContext:
// DiggStoryContext实现: /** *Context是场景的基类,以后所有的场景都需要继承Context,并实现doContext()方法 */ public class DiggStoryContext extends Context{ private Story story ; //顶贴的人 private User user; private FeedRepository feedRepository; @override public void doContext(){ //新建一个Feed,表示某个人顶了某个Story, Feed feed = new Feed(); Property property = new Property(); property.setName("title"); property.setValue("Digg the story"+diggStoryEvent.getStory().getName()); //....动态的增加新的Property feed.add(property); //保存Feed,以便让好友可以看到自己的动态 this.feedRepository.add(feed); //场景的行为以后还可能扩展 }
//利用工厂方法创建场景,保证场景的不变量,关于为什么这样做,下文会描述 public static DiggStoryContext create(Story story,User user,FeedRepository feedRepository){ DiggStoryContext diggStoryContext = new DiggStoryContext(story,user,feedRepository); return diggStoryContext;
}
}
//以下是重构以后的DiggStorySynchronizedEventProcessor public class DiggStorySynchronizedEventProcessor implements DomainEventProcessor{ private DiggStoryEventRepository diggStoryEventRepository; private FeedRepository feedRepository; //这个时候,事件处理器不会涉及到具体的业务操作,它仅仅通过调用模型对象的方法即可 public void processEvent(DomainEvent domainEvent){ DiggStoryEvent diggStoryEvent = (DiggStoryEvent)domainEvent; //存储DiggStoryEvent以便以后统计哪些人顶了Story,同时也可以进行事件回放 this.diggStoryEventRepository.add(diggStoryEvent); domainEvent.execute(); } }
//以下是DiggStoryEvent的实现: public class DiggStoryEvent extends DomainEvent{ //这里需要注意,不是每一个领域事件都有场景的 private DiggStoryContext diggStoryContext; public void execute(){ this.diggStoryContext.doContext(): } //其它的代码和以前类似
}
|
好了,最后,我们再来看看Story重构之后如何于其它的模型交互。以下是Story重构后的结构:
public class Story implements Diggable { public void digg(EventHandler eventHandler,DiggStoryContext diggStoryContext){ DiggStoryEvent diggStoryEvent = new DiggStoryEvent(this,user); diggStoryEvent.setDiggContext(diggStoryContext) diggStoryEvent.accept(eventHandler); } }
|
好了,经过重构,我们发现事件处理器的代码很简单,几乎没有任何逻辑,所有的逻辑其实都是由场景对象来实现,但是这里面我们还需要考虑一个问起,那就是场景对象的不变量约束,因为场景对象有行为和属性,行为表示当前系统发生的交互,而属性就是当前交互所设计的系统其它对象,所以此时场景对象的不变量约束很重要,而不变量约束我们一般都是通过工厂模式来解决,因此此时我们引入工厂方法来完成DiggStoryContext的创建。
OK了,重构过程结束,最后在总结以下整个系统流程。当用户进行操作的时候,首先创建场景对象,然后领域模型对象以某种角色加入到场景中来,让后场景完成具体的逻辑操作,业务操作完成,场景生命周期结束。如果领域模型还会触发事件的话,那么事件处理器会进行相应的事件处理,而这里事件处理里的实现逻辑也需要有场景对象来完成。因此模型应该实现逻辑,模型驱动技术组件完成逻辑操作。打个比方,领域模型好比我们自己,而架构就好比社会,架构为模型提供了一个大的环境,但是在这个社会中,每个人能做什么事情,还得我们自己(模型)来完成,社会是不会替我们完成什么任务的,我们的自己来。
说到这里,我们发现四色原型和DCI有很大的相似之处,四色原型中的红色MI模型应该就是当前的场景,而黄色的role模型就是模型的角色,而绿色的PPT模型就是领域实体模型.
[该贴被xmuzyu于2010-03-26 22:04修改过]