领域驱动(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 代码 实现异步观察

你可能感兴趣的:(设计模式,spring,swing,IOC,领域模型)