领域驱动(DDD)充血模式下,domain 与 Service以及Repository的解耦---DOMAIN EVENT

思考充血模式下,domain object 对 repository 的依赖的解耦方法。实际上在HTML,java swing 等中都已经给出的解决方法,就是 event。
想到解耦在GOF设计模式中最熟悉的就是 observer模式和command模式,其实两者区别不大。所以以observer模式来实现。 在java.util中就已经有了observer的实现,但是太简单了,不太适合这种复杂的domain,特别是在有多种不同的事件触发的情况.所以决定重写Observable.

public abstract class DomainObservable implements java.io.Serializable {

private HashMap<String, Collection> observerMap = new HashMap<String, Collection>();

public HashMap<String, Collection> getObserverMap() {
return observerMap;
}

public void setObserverMap(HashMap<String, Collection> observerMap) {
if(observerMap != null)
{
this.observerMap = observerMap;
}
}

public void addObserver(String eventType,DomainObserver domainObserver) {
if (domainObserver == null)
{
throw new NullPointerException();
}

if(observerMap.containsKey(eventType))
{
observerMap.get(eventType).add(domainObserver);
}
else
{
Collection<DomainObserver> observers = new ArrayList<DomainObserver>();
observers.add(domainObserver);
observerMap.put(eventType, observers);
}
}

public void deleteObserver(String eventType,DomainObserver o) {
Collection<DomainObserver> observers = observerMap.get(eventType);
if(observers != null)
{
observers.remove(o);
}
}

public void deleteObservers() {
observerMap.clear();
}

public void notifyObservers(String eventType)
{
this.notifyObservers(eventType, null);
}

public void notifyObservers(String eventType,Object arg) {
Collection<DomainObserver> observers = observerMap.get(eventType);
if(observers != null)
{
Iterator<DomainObserver> ita = observers.iterator();
while(ita.hasNext())
{
DomainObserver domainObserver = ita.next();
ObserverValueObject observerValueObject = domainObserver.getObserverValueObject();
if(observerValueObject != null)
{
observerValueObject.init(this,arg);
}
domainObserver.update(this,eventType,arg);
}
}
}
}

这样在domain object中可以发出不同类型的event,observer也可以针对不同的event来进行观察.
在来看observer interface,如下

public interface DomainObserver {


/**
* 当被观察者对象发生变化引起事件的时候,调用
* @param demainObservable 被观察者
* @param eventType 被观察者事件类型
* @param arg 被观察者传递的值
*/
public void update(DomainObservable demainObservable,String eventType,Object arg);
}

对于observer,我定义成为了去完成某项功能的client。为了完成功能,需要两个重要方面,服务和数据。服务好说 IOC一下就可以了。但是对于数据,就需要做一些操作了,目的是为了让observer与observable解耦,同时对observer进行依赖注入。所以怎样从domain object 中抽取出 observer所需要的数据,就成了observer value object的任务了。
看如下value object 接口:

public interface ObserverValueObject extends java.io.Serializable{

/**
* 根据被观察者生成观察者所需要的值对象
* @param domainObservable
* @param arg
*/
public void init(DomainObservable domainObservable,Object arg);

}

然后对observer interface 进行改进

public interface DomainObserver {

/**
* 返回观察者的值对象
* @return
*/
public ObserverValueObject getObserverValueObject();

/**
* 当被观察者对象发生变化引起事件的时候,调用
* @param demainObservable 被观察者
* @param eventType 被观察者事件类型
* @param arg 被观察者传递的值
*/
public void update(DomainObservable demainObservable,String eventType,Object arg);
}

另外,某些情况下,被观察者需要知道观察者做事情后的结果,所以需要给出一些回调方法,如下 3种回调

/**
* 回掉 具体方法 获取返回值
* @param eventType
* @param methodName
* @param args
* @return
* @throws Throwable
*/
public Object callbackDomainObserver(String eventType,String methodName,Object[] args)
{
Object result = null;
boolean isFindMethod = false;
Collection<DomainObserver> observers = observerMap.get(eventType);
if(observers != null)
{
Iterator<DomainObserver> ita = observers.iterator();
while(ita.hasNext())
{
DomainObserver domainObserver = ita.next();

/**
* 构建参数对象CLASS
*/
Class[] parameterTypes = null;
if(args != null)
{
parameterTypes = new Class[args.length];
for(int i = 0;i<args.length;i++)
{
parameterTypes[i] = args[i].getClass();
}
}

Method method = null;

/**
* 取得相同名称的方法 如果没有 就是NULL
*/
try {
method = domainObserver.getClass().getMethod(methodName, parameterTypes);
} catch (Exception e) {
}

if(method != null)
{
try {
result = method.invoke(domainObserver, args);
isFindMethod = true;
break;
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
if(e.getTargetException() instanceof SystemException)//如果是系统业务异常
{
throw (SystemException)e.getTargetException();
}
else//否则是程序错误异常
{
throw new RuntimeException(e.getTargetException());
}
}
}
}
}

if(isFindMethod)
{
return result;
}
else
{
throw new RuntimeException("System Observer Error:Domain Object("+this.getClass().getName()+") needs Observer to observe event of "+eventType+" and Need CallBack method "+methodName);
}
}


public Collection<DomainObserver> callbackDomainObserver(String eventType)
{
return observerMap.get(eventType);
}


public Collection<ObserverValueObject> callbackObserverValueObject(String eventType)
{
Collection<DomainObserver> observers = observerMap.get(eventType);
if(observers != null)
{
Collection<ObserverValueObject> valueObjects = new ArrayList<ObserverValueObject>();
Iterator<DomainObserver> ita = observers.iterator();
while(ita.hasNext())
{
valueObjects.add(ita.next().getObserverValueObject());
}

return valueObjects;
}
else
{
return null;
}
}

在我的项目中,第一种回调(回调方法)用的比较多,但这里有一些耦合。本来想再利用一种KEY-VALUE配置的方式来解耦,但是想想,这样做起来,返回代码阅读困难度增加不少,就放弃了。这里算是一个小的不足,看各位有没有更改好的解决办法。

到了这里,大部分代码工作已经完成,下面就需要整合了。其实里面最关键也最重要的就是怎么样把observer注入到 被观察者中。
首先配置observer value object 和 observer --- spring 里面很好配置,注意:有些observer可以直接配置成 单例,但是有些需要给出回调方法的observer 不能配置成 单例 。
然后是把observer注入到被观察者中。利用aspectJ(spring 里面已经整合过)的LTW功能注入。
下面给出个例子吧
domain object

@Configurable
public class BrandDomain extends DomainObservable{
private String brandno;

private String brandName;

public String getBrandName() {
return brandName;
}

public void setBrandName(String brandName) {
this.brandName = brandName;
}

public String getBrandno() {
return brandno;
}

public void setBrandno(String brandno) {
this.brandno = brandno;
}

public void addBrandDomain(BrandValueObject valueObject)
{
if(!this.checkSameObjectExist(valueObject.getBrandno()))
{
this.setBrandno(valueObject.getBrandno());
this.setBrandName(valueObject.getBrandName());
this.notifyObservers("save");
}
else
{
throw new BusinessException("品牌编码已经存在");
}
}

public void editBrandDomain(BrandValueObject valueObject)
{
this.setBrandName(valueObject.getBrandName()); this.notifyObservers("update");
}

protected boolean checkSameObjectExist(Object keyword)
{
this.notifyObservers("checkSameObject", keyword);
return (Boolean) this.callbackDomanObserver("checkSameObject", "isExistSameObject", null);
}

public void deleteBrand()
{
this.notifyObservers("delete");
}
}


配置文件

<bean class="cn.com.dbSale.server.business.domain.basisDomain.productDomain.brandDomain.BrandDomain" scope="prototype" >
<property name="observerMap">
<map key-type="java.lang.String" value-type="java.util.Collection">
<entry key="save">
<list value-type="java.util.ArrayList">
<ref bean="saveObjectObserver"/>
</list>
</entry>
<entry key="update">
<list value-type="java.util.ArrayList">
<ref bean="updateObjectObserver"/>
</list>
</entry>
<entry key="delete">
<list value-type="java.util.ArrayList">
<ref bean="deleteObjectObserver"/>
</list>
</entry>
<entry key="checkSameObject">
<list value-type="java.util.ArrayList">
<ref bean="checkSameObjectObserver"/>
</list>
</entry>
</map>
</property>
</bean>
<bean id="saveObjectObserver" class="cn.com.dbSale.server.business.observer.observerInterface.SaveObjectObserver">
<property name="repositoryInterface">
<ref local="repositoryInterface"/>
</property>
</bean>

<bean id="updateObjectObserver" class="cn.com.dbSale.server.business.observer.observerInterface.UpdateObjectObserver">
<property name="repositoryInterface">
<ref local="repositoryInterface"/>
</property>
</bean>

<bean id="deleteObjectObserver" class="cn.com.dbSale.server.business.observer.observerInterface.DeleteObjectObserver">
<property name="repositoryInterface">
<ref local="repositoryInterface"/>
</property>
</bean>

<bean id="checkSameObjectObserver" class="cn.com.dbSale.server.business.observer.observerInterface.CheckSameObjectObserver" scope="prototype">
<property name="repositoryInterface">
<ref local="repositoryInterface"/>
</property>
</bean>

以上是一个最简单的例子,还算是表达出了基本意思。
对于复杂的流程可以通过发布事件 达到流程可配置.
增、删、改 这3个观察者 可以适注入到所有DOMAIN 对象中。
另外observer value object 的用法,没有给出,各位可以自己试试

整个DOMAIN EVENT 的源文件 都在附件里面,代码不多。
本人第一次网上发文章,如有不足,多多关照!
还有,如果各位需要observer 与 被观察者 异步操作,可以修改 AbstractObservable 代码 实现异步观察
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值