OFBizentityengine中的设计模式总结



最近同时在看《Core J2EE Patterns》跟ApacheOFBiz 源码,确实正如OFBiz官方介绍的那样,OFBiz应用了该书中的很多经典的设计模式。本篇结合OFBiz的源码试图总结一下其中用到的几个典型的Patterns。



# 典型的J2EE模式

## 业务代表模式
业务代表模式主要目的是用于隐藏业务逻辑对于调用端的实现,消除不同层次之间的耦合,它封装了业务服务的访问。

OFBiz中对于业务代表模式的实现令人印象深刻,因为它在service跟entity engine层都实现了该模式。这里我们只讨论entity engine中的实现。

在org.ofbiz.entity下有一个Delegator接口,它就是其他层跟持久层打交道的入口。
OFBiz默认给出了一个实现:GenericDelegator:



Delegator几乎提供了对所有数据访问需求的抽象,包括:数据缓存管理、事务管理、主键管理、查找、创建、更新、删除等。这有些颠覆我们想象中的开发模式,没错,这种方式跟我们通常的开发方式有些不一样。我们通常会基于数据库的设计来构建entity,然后基于每个entity构建DAO(或者把这个任务交给ORM框架)。但OFBiz不是这么做的(想象一下,如果它这么做了,它也实现不了现在的业务代表模式),在OFBiz entity engine中,并没有针对o-r mapping构建出来的entity,取而代之的是一个通用的、泛化的实体,而且不仅仅是如此,这里包括sql语句的组成部分都是被抽象过之后形成的“语义层”,通过XML配置来完成实体、关系的定义。这才给了OFbiz实现业务代表模式的可能。

它如何实现用一个业务代表,代理了所有数据访问?

我们看到它有两个名为getEntityHelper方法的重载,它们都返回GenericHelper对象。这里我们需要来了解几个entityengine内部的辅助对象:

GenericDAO:实现了基本的数据访问操作的(CRUD)。这其中,每个方法都需要具体去跟数据库通信,通过什么?通过SQLProcessor对象

SQLProcessor:SQLProcessor负责每次SQL的执行

GenericHelper:通常一个系统里对于数据的访问/操作并不仅仅只是简单的CRUD,其中还包含有更复杂的操作(比如关系查询、条件删除等),因此OFbiz为这些相对复杂的操作抽象出了GenericHelper。

GenericHelperDAO:是对GenericHelper的实现,其内部借助于GenericDAO,来实现这些相对复杂的数据访问。
就此我们基本理清了Delegator对于数据访问的脉络。

## 传输对象模式

传输对象模式之前称之为ValueObject模式(值对象模式)

传输对象模式用于解决跨层次传输多种数据的问题。

在一些系统中,会有不同层次的两个组件通信(集成层与业务层或表现层与业务层)的场景,两个组件之间有可能是采用EJB实现的远程调用,如果你需要获取的不是单个实体的,而是一组数据(比如在客户端展示的信息,有可能是基于多个实体的并集)。多次调用远程对象的getter方法,很明显会增加网络负载并且降低系统性能。而传输对象就是用来解决这一问题的

在OFBiz中,可以看到对传输对象模式的应用:


这种应用非常广泛,因为在OFbiz中并没有对每种对象构建一个javabean,因而也就不存在object-relationalmapping(orm)的说法,它采用上面这几个对象表示了所有从数据库获取到的数据的表示(也就是采用了通用实体的方式),而且这几个通用对象都包含了数据访问行为(也就是说OFbiz遵循了j2eeejb 中的entitybean的设计方式,是充血模型的实体,而非只是数据库对象的载体)。

其中:

GenericEntity:是一个实现了Value Object模式(传输对象的前称)的基类;

GenericValue:可以看做从数据库获取到的任意一条数据行记录(实现了多重复合传输对象模式);

GenericPK:用于表示主键对象;
下图展示了GenericEntity的类图:



可以看到它实现了Serializable,因此它是可以串行化到客户端,被当成本地对象来使用的(而不仅仅是本地引用,因为它包含的fields集合已经作为一个Map传输到客户端来了)。

它继承了Observable,用于接收客户端对实体的更新通知,如果客户端对实体进行了更新,将会通过setFields方法进行更新(setFields方法是个粗粒度的update方法,这避免了采用频繁调用细粒度的setter方法导致的网络开销)。因此它是一个可更新的传输对象。

另外,它实现了Map<String,Object> 包装了内部的Map。事实上,它只是对真实的数据实体的一种包装(真实的数据实体是ModelEntity),它只是通过必备的ModelEntity来实例化自身,而它自身被实现为“传输对象”。