视图对象(VO、DTO)的应用!
?目录
?
VO ( View Object ): 视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
DTO ( Data Transfer Object ): 数据传输对象,这个概念来源于 J2EE 的设计模式,原来的目的是为了 EJB 的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
PO ( Persistent Object ): 持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应 PO 的一个(或若干个)属性。 ?
VO 与 DTO 的区别
??大家可能会有个疑问:既然 DTO 是展示层与服务层之间传递数据的对象,为什么还需要一个 VO 呢?对!对于绝大部分的应用场景来说, DTO 和 VO 的属性值基本是一致的,而且他们通常都是 POJO ,因此没必要多此一举,但不要忘记这是实现层面的思维,对于设计层面来说,概念上还是应该存在 VO 和 DTO ,因为两者有着本质的区别, DTO 代表服务层需要接收的数据和返回的数据,而 VO 代表展示层需要显示的数据。
简化传值
主要用于减少数据传送次数,常用在SSH中。一般采用数据传输对象工厂来满足这种模式的设计需求,它可以帮助将客户端与域模型分离。视图对象是一个普通的Java类,它封装了要传送的批量的数据。当客户端需要读取服务器端的数据的时候,服务器端将数据封装在视图对象中中,这样客户端就可以在一个网络调用中获得它需要的所有数据。使用视图对象的时候,一个主要问题是建立什么样的视图对象:这个对象能够容纳哪些数据,对象的结构是什么,这个对象的数据是如何封装的。
视图对象是服务器端和客户端进行通信的一个协议格式,合理的DTO设计将会使得服务器和客户端的通信更加顺畅。
?
分布式应用(跨域)
?
?
?
在分布式系统中,PO完全位于服务器端。根据持久化对象可否直接传递到客户端,域对象可以分为两种类型:一种是服务器端的持久化对象不可以直接传递到客户端,比如EJB中的实体Bean是不能被传递到客户端的;一种是持久化对象可以直接传递到客户端,比如Hibernate中的PO变为detached object以后就可以传递到客户端。
域模型结构可以在一次网络调用中复制到客户端,客户端可以读取、更新这个对象而不需要额外的网络调用开销,而且客户端还可以通过将更新后的视图对象回传到服务器端以更新数据易于实现快速开发。通过使用域视图对象可以直接将域模型在层间传输,减少了工作量,可以快速地构建出一个应用。
?
可定制的视图对象,使它仅封装客户端需要的数据的任意组合,完全与服务器端的域模型相分离。定制对象与域对象的区别就是它不映射到任何服务器端的域模型。
定制视图对象主要用于只读操作,也就是视图对象只能用来显示,而不能接受改变。既然定制视图对象仅仅是一个数据的集合,和任何服务端对象没有必然的关系,那么对定制视图对象进行更新就是没有意义的了。
常见的方式就是为实体对象建立一个Model类,该类只包含这个对象所有字段的getter和setter。例如用户User实体有name, password字段,则建立一个UserModel类,public方法有getName,setName,getPassword,setPassword。这样就可以把UserForm作为参数传给其他函数。
?
Data Transfer Object(DTO)或VO模式是为了解决这样的问题:例如我们的一个实体Bean,其对应的数据库表的字段非常多,那么我们在其Home接口的create方法中以及Enterprise Bean类的ejbCreate方法中的参数可能就会很多,导致我们的这些方法不够elegant。我们可以通过定义一个简单的Java类(实现Serializable接口),其中定义一些属性,并提供相应的get和set方法来解决上面的问题。
?
使用视图对象来封装业务数据,并且有一个单独的方法可以用来获取或者发送这个VO,当客户端请求一些业务数据的时候,服务端就可以产生出一个VO,并且将它赋值,最后,可以用传值的方法传递给客户端。
这样的好处是减少网络对话,加速层之间的数据交流,我觉得,这样更大的好处是使程序逻辑更加清楚,更加面向对象。写VO的时候,要实现Serializable接口。
public UserVO implements Serializable {
???? private String loginId;
???? private String password;
???? private String name;
???? private String role;
???? ... ...
}
serialization 允许你将实现了Serializable接口的对象转换为字节序列,这些字节序列可以被完全存储以备以后重新生成原来的对象。?
serialization不但可以在本机做,而且可以经由网络操作(就是猫小说的RMI)。这个好处是很大的----因为它自动屏蔽了操作系统的差异,字节顺序等。比如,在Window平台生成一个对象并序列化之,然后通过网络传到一台Unix机器上,然后可以在这台Unix机器上正确地重构这个对象。
Object serialization主要用来支持2种主要的特性:
1、Java的RMI(remote method invocation).RMI允许象在本机上一样操作远程机器上的对象。当发送消息给远程对象时,就需要用到serializaiton机制来发送参数和接收返回直。
2、Java的JavaBeans.?? Bean的状态信息通常是在设计时配置的。Bean的状态信息必须被存起来,以便当程序运行时能恢复这些状态信息。这也需要serializaiton机制。
总之如果在网络的环境下做类传输,应该还是implements Serializable。
如果说数据访问技术是一些得心应手的兵器的话,数据访问模式就应该是各种武功秘笈了,它们才是致胜的法宝。架构师在不同的应用场合下可能会选择不同的数据访问模式,并且还会不断地推陈出新,这里不会也不可能穷尽所有的数据访问模式,而只是列举了其中最为典型的几个。
在线访问
?
Data Access Object(DAO)
?
一个典型的 DAO 实现通常有以下组件:
一个 DAO 工厂类
一个 DAO 接口
一个实现了 DAO 接口的具体类 数据传输对象(有时称为值对象) 这当中具体的 DAO 类包含访问特定数据源的数据的逻辑。
DTO模式
?
?
?
方法一(完全相同的对象之间传值)
属性复制可以通过Apache Jakarta Commons Beanutils组件提供的属性批量复制功能,避免繁复的get/set操作。down下来之后,里面的API DOC一应俱全。
(http://jakarta.apache.org/commons/beanutils/)
对于一些无需处理其它处理(如过滤)直接用BeanUtilsBean.copyProperties方法,其参考如下:
public? static?? void? copyProperties(java.lang.Object dest,? java.lang.Object orig)????????? throws?????????? java.lang.IllegalAccessException,
? // java.lang.reflect.InvocationTargetExceptioCopy property values from the origin bean to the destination bean for all cases where the property names are the same.
?
可以看到,在方法1中通过方法copyProperties的时候,二者之间在的属性名必须相同(Copy property values from the origin bean to the destination bean for all cases where the property names are the same)。
方法一范例
TUser user? =?? new? TUser();
TUser anotherUser? =?? new? TUser();
user.setName( " Emma " );
user.setUserType( 1 );
?try??? {
BeanUtils.copyProperties(anotherUser,user);
System.out.println( " UserName =>? "
? + anotherUser.getName()
);
System.out.println( " UserType =>? "
? +? anotherUser.getUserType()
);
}??? catch? (IllegalAccessException e)?? {
e.printStackTrace();
}??? catch? (InvocationTargetException e)?? {
e.printStackTrace();
}?
?
方法二(不完全相同的对象之间传值)
方法二通过 Object value = PropertyUtils.getProperty(orig, destDesc[i].getName());
? PropertyUtils.setProperty(dest, destDesc[i].getName(), value);
也是将源与目的之间copy相同的属性名。而VO是在前台显示,所以难免会用到PO中所不存在的属性值。比如PO中可能是一个对象,而VO中则可能是此对象的全部属性。其中的一些转换则需要依据前台需 要针对性地处理啦!
?
范例二
/** //*
?* Created on 2006-4-26
? */?
? package? com.util;
?
?import? java.beans.PropertyDescriptor;
?import? java.util.Collection;
?
?import? org.apache.commons.beanutils.PropertyUtils;
?
?
? /**?? */?? /**
?* CopyUtil
?*? @author? Jkallen
? */?
?? public?? class? CopyUtil?? {
???
????? /**?? */?? /**
???? * Copy properties of orig to dest
???? * Exception the Entity and Collection Type
???? *? @param? dest
???? *? @param? orig
???? * ?@return? the dest bean
????? */?
????? public?? static? Object copyProperties(Object dest, Object orig)?? {
???????? if? (dest? ==?? null?? ||? orig? ==?? null )?? {
???????????? return? dest;
??????? }?
???????
??????? PropertyDescriptor[] destDesc? =? PropertyUtils.getPropertyDescriptors(dest);
???????? try??? {
???????????? for? ( int? i? =?? 0 ; i? <? destDesc.length; i ++ )?? {
??????????????? Class destType? =? destDesc[i].getPropertyType();
??????????????? Class origType? =? PropertyUtils.getPropertyType(orig, destDesc[i].getName());
???????????????? if (destType? !=?? null?? &&? destType.equals(origType)
???????????????????????? &&?? ! destType.equals(Class. class ))?? {
???????????????????? if ( ! Collection. class .isAssignableFrom(origType))? {???????????????????
???????????????????????? try? {
??????????????????????????? Object value? =? PropertyUtils.getProperty(orig, destDesc[i].getName());
??????????????????????????? PropertyUtils.setProperty(dest, destDesc[i].getName(), value);
??????????????????????? }? catch (Exception ex)? {???????????????????????????
??????????????????????? }?
??????????????????? }?
??????????????? }?
??????????? }?
???????????
???????????? return? dest;
??????? }? catch (Exception ex)?? {
???????????? throw?? new? CopyException(ex);
?//???????????? return dest;
????????? }?
??? }?????
???
????? /**?? */?? /**
???? * Copy properties of orig to dest
???? * Exception the Entity and Collection Type
???? *? @param? dest
???? *? @param? orig
???? *? @param? ignores
???? *? @return? the dest bean
????? */?
????? public?? static? Object copyProperties(Object dest, Object orig, String[] ignores)?? {
???????? if? (dest? ==?? null?? ||? orig? ==?? null )?? {
???????????? return? dest;
??????? }?
???????
??????? PropertyDescriptor[] destDesc? =? PropertyUtils.getPropertyDescriptors(dest);
???????? try??? {
???????????? for? ( int? i? =?? 0 ; i? <? destDesc.length; i ++ )?? {
???????????????? if? (contains(ignores, destDesc[i].getName()))?? {
???????????????????? continue ;
??????????????? }?
???????????????
??????????????? Class destType? =? destDesc[i].getPropertyType();
??????????????? Class origType? =? PropertyUtils.getPropertyType(orig, destDesc[i].getName());
????????? ???????if (destType? !=?? null?? &&? destType.equals(origType)
???????????????????????? &&?? ! destType.equals(Class. class ))?? {
???????????????????? if ( ! Collection. class .isAssignableFrom(origType))? {
??????????????????????? Object value? =? PropertyUtils.getProperty(orig, destDesc[i].getName());
??????????????????????? PropertyUtils.setProperty(dest, destDesc[i].getName(), value);
??????????????????? }?
??????????????? }?
??????????? }?
???????????
???????????? return? dest;
??????? }? catch (Exception ex)?? {
???????????? throw?? new? CopyException(ex);
??????? }?
??? }?
???
???? static?? boolean? contains(String[] ignores, String name)?? {
???????? boolean? ignored? =?? false ;
???????? for? ( int? j? =?? 0 ; ignores? != ??null?? &&? j? <? ignores.length; j ++ )?? {
???????????? if? (ignores[j].equals(name))?? {
??????????????? ignored? =?? true ;
???????????????? break ;
??????????? }?
??????? }?
???????
???????? return? ignored;
??? }?
}?
?
?