智能领域对象设计(演绎革命)-1/2

请先看看:智能领域对象设计

本文源代码
关于智能领域对象的设计,一直没有拿出确实的例子来说明这样编程的好处和优点,以及如何正确地理解这种编程方式。接下来我开始从传统Service + DAO开发模式开始改造和发展,直到变化成智能领域对象设计的开发模式上来,对于每一种变化,我会统计出手工代码编写行数(setter 、getter和import等就不在统计范围内了),看看生产力的变化。任何生产力的提高都是体现在机械代替重复而又规律的工作上,我们选择的案例同样是本应该被工业化掉的东西,但还在手工劳作。
传统的Service + DAO包括三个核心类,以“用户”对象为例,通常包括的类:User, UserService, UserDAO。通常需要的外部支持是:hibernate/jpa,spring。为了演示方便忽略掉接口(如IUserService),也不再区分PO和VO。事实上案例演示完成后,你会发觉这两个东西确实很少使用。
这个实例中包含了一个持久层框架Thin,我先将有关持久的论述写在这里,你可以先看实例回头有兴趣再这段内容:所谓对象的持久就是把对象的属性登记在数据库中,在现实生活是经常发生的,如我们去银行办一个储蓄卡,需要填表,而填表的过程就是持久化的过程。看看这张表格,便会发现所有填写项都可以用key-value表示,再考察我们是如何区分现实对象,便会发现同样是以对象的属性为区分依据,属性就可以用key-value表示,所以无论任何对象只要被持久必然可转化成key-value,唯一的不同就是key-value的存储方法不同而已,即:key-value是一切对象持久的接口。如果应用程序的持久方式是基于key-value的,那么这种应用不仅便于更换不同的关系数据库,即使是往NO-SQL数据库上移植,纵然我对No-SQL数据库不甚了解,但它绝不会偏离本质。Thin就是这么一个工具,把对象转化成key-value,然后存入对应的表,反之亦可。科学家通常用习惯用“美”来衡量结论正确性,虽然没有什么科学依据,但也屡试不爽。key-value是一切对象持久的接口,这个结论是美的,大家可以顺便考量一下thin的短小精干是否也符合美的标准。”大道若简”,我相信基于key-value的持久方式,正是持久层的“大道”。
User类改动前User.java:


@Entity
@Table(name=”user”)
public class User{
@Id
private String uid;
private String uname;
private String password;
private String gid;//机构ID
private String gname;
//机构名称
private String rid;
//角色ID
private String rname;
//角色名称

//setter and getter
}

UserService.java

/**
* spring的配置忽略
*
* <bean id="UserService" class="com.icitic.zero.service.impl.UserService">
<property name="userDao" ref="userDao"/>
</bean>

* @author haihong
*
*/

public class UserService {
private UserDAO userDao;
public void addUser(User user) {
try {
this.userDao.add(user);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public User getUser(String id) {
try {
this.userDao.getUserById(id);
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}

public void updateUser(User user) {
try {
this.userDao.update(user);
} catch (Exception e) {
e.printStackTrace();
}
}
public void deleteUser(Collection<User> users) {
try {
User[] us = new User[users.size()];
users.toArray(us);
this.userDao.delete(us);
} catch (Exception e) {
e.printStackTrace();
}
}
public void saveOrUpdate(User user) {
try {
this.userDao.save(user);
} catch (Exception e) {
e.printStackTrace();
}
}
public UserDAO getUserDao() {
return userDao;
}
public void setUserDao(UserDAO userDao) {
this.userDao = userDao;
}
}
UserDAO.java
/**
* <bean id="UserDAO" class="com.icitic.zero.dao.UserDAO">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
*
*
* @author haihong
*
*/

public class UserDAO extends HibernateDaoSupport{
public void add(User user) {
getHibernateTemplate().save(user);
}
public User getUserById(String id) {
return (User) getHibernateTemplate().load( User.class, id);
}
public void update(User user) {
getHibernateTemplate().update(user);
}
public void delete(User... user) {
getHibernateTemplate().delete(user);
}
public void deleteById(String uid) {
User user = this.getUserById(uid);
getHibernateTemplate().delete(user);
}
public void save(User user) {
getHibernateTemplate().saveOrUpdate(user);
}
}



1.编码量统计:
User.java 11行
UserService.java 56行
UserDAO.java 21行
共88行

开始用thin(基于key-value的持久层框架)改造:
User.java


@BeanTableName(name="z_user")
public class User{
@Primary
private String uid;
private String uname;
private String password;
private String gid;
//机构ID
private String gname;
//机构名称
private String rid;
//角色ID
private String rname;
//角色名称
//setter and getter
}


替换类和主键的注解换成thin的注解,其他不变.
UserService.java


public class UserService extends BaseService {
public void addUser(User user) {
try {
this.add(user);
} catch (Exception e) {
e.printStackTrace();
}
}
public User getUser(String id) {
try {
List users = this.getObject(User.class,SQLCriterion.get("uid", id));
if(users.isEmpty()){
return (User)users.get(0);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void deleteUser(User user) {
try {
this.deleteObject(user);
} catch (Exception e) {
e.printStackTrace();
}
}
public void deleteUser(String uid) {
try {
User user = (User)this.getObject(User.class, SQLCriterion.get(
"uid", uid));
this.deleteUser(user);
} catch (Exception e) {
e.printStackTrace();
}
}
public void deleteUser(Collection<User> users) {
Collection<String> uids = new ArrayList();
for (User u : users) {
uids.add(u.getUid());
}
try {
getBeanTable(User.class).delete(SQLCriterion.get(
"uid", Operator.IN, uids));
} catch (Exception e) {
e.printStackTrace();
}
}
public void saveOrUpdate(User user) {
try {
ThinContext.ctx.beginTransaction();
this.deleteUser(user);
this.addUser(user);
ThinContext.ctx.commitTransaction();
} catch (SQLException e) {
try {
ThinContext.ctx.rollback();
} catch (SQLException e1) {
}
throw new RuntimeException(e);
}
}
}


UserDAO.java不再需要,所有类似与此的DAO都不需要了,BaseService 已经为对象的持久提供好了接口,只需要调用即可。

2.编码量统计:
User.java 11行
UserService.java 60行
UserDAO.java 0行
编码量合计:71行

如此改进会减少88-71=17行代码和一个Dao类。
同时可以放一放hibernate/jpa这个朋友了,而且可以减少与spring的瓜葛。说真的,使用Service+DAO这种方式编程,有类似spring这样的管理工具,还是真是轻松了不少,但是spring所提供的依赖管理方式只是让我们更加舒适地适应了传统,但是原来要写程序现在几乎还要写。

我们再进一步用采用面向对象的思想(智能领域对象设计)来改造这个模块,让有User继承ThinObject:


@BeanTableName(name="z_user")
public class DomainUser extends ThinObject{
@Primary
private String uid;
private String uname;
private String password;
private String gid;
//机构ID
private String gname;
//机构名称
private String rid;
//角色ID
private String rname;
//角色名称
//所有的增删改查父类已经提供了
//setter and getter
}

不再需要UserServiceT.java了,像这种增删改查的功能,ThinObject已经提供了。

3.编码量统计:
DomainUser.java 11行
UserServiceT.java 0行
UserDAO.java 0行
合计:11行

代码编写量从88行降到11行。减少87.5%的工作量。以30类为例,就少写2310行代码,少建60个类.
大家可以根据自己的工作效率算算,会节省多少天的工作量。从此单表的DAO操作就可以不用再写,也不用建立对应增删改查的类了。
当然这个例子有些极端,但是并不影响它在提高生产力的方面的不错表现。读到这里你可以会有一个疑问,我们应该如何处理对象之间的关系,如一对多,或者多对多。复合对象是由简单对象组成的,既然简单的存储已经很方便地解决了,复合对象也水到渠成地解决了。
智能领域对象设计还有个特性,是支持因果事件模式,模拟因果传递。我在下一篇实例中详解!

下一篇:智能领域对象设计(实例讲解)

[该贴被cuwkuhaihong于2010-08-09 13:20修改过]

这么经典的东西,竟然沉底了

2010年08月08日 18:45 "cuwkuhaihong"的内容
我相信基于key-value的持久方式,正是持久层的“大道”。 ...

这个我同意,其实万事万物都有ID标识,一个ID标识一个对象,MT的分析模式中以及DDD中都开篇肯定了这点,是基本规律。

2010年08月11日 11:26 "banq"的内容
2010年08月08日 18:45 "cuwkuhaihong"的言论
我相信基于key-value的持久方式,正是持久层的“大道”。 ...


这个我同意,其实万事万物都有ID标识,一个ID标识一个对象,MT的分析模式中以及DDD中都开 ...

banq很会赞扬人。
其实我想知道你对此不同的看法。

看起来很不错哦!
有几个问题:
1、数据库链接怎么控制?
2、事务方面怎么控制?
3、一些场合肯定是需要sql语句的,写到 service 或者 domain object 里?

当然,最不爽的是,领域对象必须继承一个 ThinObject,这就不是领域对象了,
换句话说,离开 thin,这个领域对象就玩不转了,这并非领域对象的初衷。

2010年08月11日 22:09 "yananay"的内容
看起来很不错哦!
有几个问题:
1、数据库链接怎么控制?
2、事务方面怎么控制?
3、一些场合肯定是需要sql语句的,写到 service 或者 domain object 里?

当然,最不爽的是,领域对象必须继承一个 ThinObjec ...


这些问题提的好,我来逐个回答。
关于数据库的链接由ThinContext控制,目前默认的采用ThreadLocal来管理链接,见代码:


public abstract class DefaultThinContext extends ThinContext {
private static ThreadLocal<Connection> connections = new ThreadLocal();
public DefaultThinContext(String schema) {
super(schema);
this.regist(this);
}
public void holdConnection(Connection conn) {
connections.set(conn);
}
public Connection getHoldConnection() {
return connections.get();
}
}

用户可以根据需要直接继承ThinContext自己管理数据库链接.关于事务,我并没有像spring那样做AOP等等的封装,只是让它一目了然地容易使用,用户可以轻松获取Connection并进行设置。如下面的代码:
//事务的使用


public void foo() {
try {
ThinContext.ctx.beginTransaction();
//todo anything
ThinContext.ctx.commitTransaction();
} catch (SQLException e) {
try {
ThinContext.ctx.rollback();
} catch (SQLException e1) {};
}finally{
//如果采用opensession的方式,就可以等到请求处理完成后统一关闭
try {
ThinContext.ctx.cleanHoldConection();
} catch (SQLException e) {}
}
}


用户可以根据情况自己进行AOP或者回调封装。采用智能领域对象编程,是要求在做对象设计的时候,每一对象都独立完成自己份内的工作。事务可复杂,也可简单,但无论多么复杂事物都是由简单的工作组成,简单工作由智能领域对象提供。所以复杂工作就是从高层协调几个对象各自完成简单工作,复杂的工作建议写在service方法里(参看智能领域对象设计2/2

结构图:
(感觉和jdon framework类似)

接下来说说让人感觉不爽的ThinObject,ThinObject提供了与数据库交互的功能,继承ThinObject可以不用在写简单对象的增删改查了,若什么都不继承,这种存取能力从而来呢。另外ThinObject只是智能领域对象设计的一个实现,如果想采用其他的持久方式并且能做到一旦继承就不用再写任何持久代码,也可以呀,只需要继承IObject即可。至于继承ThinObject是不是严格意义上领域对象,我并没有在意,无论是什么方法或理念只要能有效地提高生产力都是可取的,相反某种方法或理念阻碍了生产力的发展,都是需要改良的。


[该贴被cuwkuhaihong于2010-08-16 13:31修改过]

2010年08月11日 15:50 "cuwkuhaihong"的内容
banq很会赞扬人。
其实我想知道你对此不同的看法 ...

我对你的框架可能没有全面了解,如果有什么不同看法,从你文字看来,你的着眼点还是在解决持久化问题,你在探索一条持久化的大简之道,而我认为可能没有必要,就象我们打字,没必要关心保存文档是如何保存持久,智能模型如何持久化这事情不应该摆到台面上,是背后的事情,也不应该作为领域对象的重点职责来关注。


[该贴被banq于2010-08-17 14:29修改过]

2010年08月17日 14:29 "banq"的内容
我对你的框架可能没有全面了解,如果有什么不同看法,从你文字看来,你的着眼点还是在解决持久化问题,你在探索一条持久化的大简之道,而我认为可能没有必要,就象我们打字,没必要关心保存文档是如何保存持久,智能模型如何持久化这事情不应该摆到台面上,是 ...

处理任何问题时,只要处理方法没有从本质着手,这种方法就有可优化的地方。不能因为hibernate/jpa等持久方式已经处理这个问题,而持久化的问题停止发展,况且还有大量程序员在写持久化的程序,所以更需要让大家解放出来。关于智能模式持久化是否“应该”摆到桌面(我是放在领域对象里)的问题,是要一定的理论支持或者判断标准来说明的,不能单凭感觉或者经验。进一步思考理证如何证明领域对象不应该具有持久化的功能呢?我还没有想出确实可靠的证明,所以我就以生产力这个刚性指标的提升与否作为判断标准。不知你对我这番话有何异议。

2010年08月24日 13:39 "cuwkuhaihong"的内容
况且还有大量程序员在写持久化的程序,所以更需要让大家解放出来。 ...

你的出发点是好的,这是肯定的,这条路也是值得探索的,相当于在现有模式下的改良。

我的意见是颠覆,通过理念改变,让大家不要面向数据库编程,忽视持久化,但是得有一种好的框架支持,比如NoSQL之类是探索,你的框架也是在关系数据库上持久化探索。

不过由于对象和关系数据库的永恒矛盾性,也就是Mismatch不匹配和阻抗,试图在关系数据库上进行对象化的自动化持久(或者比JPA等更简单方式)似乎不太可能,我只是说从逻辑上看。

所以,现实中有些自动化持久框架实际是一种通融,比如去除关系数据库的关系特性,都使用key-value方式;或者去除对象特性,使用纯数据来替代。

事务处理方面,不如spring,ejb方便,自己写事务的方式太落后了,需要改进。

关于 ThinObject 的方面,或许 Qi4j 是一个学习的例子,可以参考一下。

2010年08月29日 00:37 "yananay"的内容
事务处理方面,不如spring,ejb方便,自己写事务的方式太落后了,需要改进。 ...

手工开启事务和关闭事务是没有spring等方便,但与其相比,无论是代码编写量还是类的引入量,都有很大的悬殊。如果少写50%以上的代码,但是在有使用事务的需要时要手工开启,也不失为一种选择。

其实关于是否引入事务,我一直比较纠结。从目前业界流行的处理方式来看使用事务都没有脱离容器。既然走到这一步不妨探索一下脱离容器的事务管理方式。

谢谢你的建议

"手工开启事务和关闭事务是没有spring等方便,但与其相比,无论是代码编写量还是类的引入量,都有很大的悬殊。如果少写50%以上的代码,但是在有使用事务的需要时要手工开启,也不失为一种选择。"

你的意思是你的方式更简单一些?不会吧,明明 spring,ejb 的方式更简单。

不用纠结了,增加一个 ioc 的接口,默认 spring 为实现方式,嘿嘿。

2010年08月30日 00:54 "yananay"的内容
你的意思是你的方式更简单一些?不会吧,明明 spring,ejb 的方式更简单。

不用纠结了,增加一个 ioc 的接口,默认 spring 为实现方式,嘿嘿。 ...

经过我的思考,还是不加任何事物,因为智能领域对象和spring的事务没有任何冲突,采用这种编程方式与传统的编程方式不同在于是否对业务逻辑进行对象粒度划分。传统方式是Service里写详细的业务逻辑;而采用智能领域对象设计是service协调多个对象完成业务逻辑,简单的业务逻辑,即不需要多个对象配合完成的,一个智能领域对象就够了,这样的service同样可以依托spring去管理事务。
至于简单,你看看,如果一个User对象继承了ThinObject,只需要new出来一个对象。增删改查就全部具有了。


// 在此输入java代码
User user = new User();
user.forget();
//删
user.freshRemember();
//增
user.update();
//改
user.associate();
//查

你如何比这更简单?