我們先來看一張數據扭轉的圖,這個是DDD思想下各種對象轉換的過程。
VO(View Object):視圖對象,用於展示層,它的作用是把某個指定頁面(或組件)的所有數據封裝起來。
DTO(Data Transfer Object):數據傳輸對象,用於展示層與服務層之間的數據傳輸對象。
DO(Domain Object):領域對象,就是從現實世界中抽象出來的有形或無形的業務實體。
PO(Persistent Object):持久化對象,它跟持久層(通常是關係型數據庫)的數據結構形成一一對應的映射關係,如果持久層是關係型數據庫,那麼,數據表中的每個字段(或若干個)就對應PO的一個(或若干個)屬性。
VO層我們先不看,從DTO開始,我們梳理簡單一個請求流程中數據會轉換幾次:
- 請求時用戶接口層DTO轉DO
- 請求時領域層DO轉PO
- 返回時領域層PO轉DO
- 返回是DO轉DTO
我們先看下操作之前和操作之後的代碼,在來詳解:
public static void main(String[] args) {
//初始化對象
UserDto userDto = new UserDto();
userDto.setId(1);
userDto.setName("java圈");
////////////////////////////
//UserDto轉成對象UserDo
UserDtoToUserDo userDtoToUserDo = new UserDtoToUserDo();
UserDo userDo = userDtoToUserDo.convert(userDto);
//UserDo轉成對象UserPo
UserDoToUserPo userDoToUserPo = new UserDoToUserPo();
UserPo userPo = userDoToUserPo.convert(userDo);
//UserPo轉成對象UserDo
UserPoToUserDo userPoToUserDo = new UserPoToUserDo();
UserDo userDo1 = userPoToUserDo.convert(userPo);
//UserDo轉成對象UserDto
UserDoToUserDto userDoToUserDto = new UserDoToUserDto();
UserDto userDto1 = userDoToUserDto.convert(userDo1);
System.out.println("改造之前的結果:" userDto1);
////////////////////////////
UserDo userDo2 = AssemblerFactory.getInstance().execute(UserDtoToUserDoAssembler.class,userDto, UserDo.class);
UserPo userPo1 = AssemblerFactory.getInstance().execute(UserDoToUserPoAssembler.class,userDo2, UserPo.class);
UserDo userDo3 = AssemblerFactory.getInstance().execute(UserPoToUserDoAssembler.class,userPo1, UserDo.class);
UserDto userDto2 = AssemblerFactory.getInstance().execute(UserDoToUserDtoAssembler.class,userDo3, UserDto.class);
System.out.println("改造之後的結果:" userDto2);
}
這段代碼用兩種方式實現了上述的四個對象的轉換:
第一種
- 直接通過創建對象的方式進行數據扭轉,可讀性較差,容易混亂;
- 創建流程較麻煩,需要創建8個對象,佔用額外的內存空間
- 可擴展性較差,牽一髮而動全身;
第二種
- 通過工廠模式 代碼模式 單例模式設計思想去實現,符合SOLID原則;
- 基於接口做實現代理,符合低耦合的概念;
- 可讀性較強,每一個轉換隻要一行代碼,只要清楚轉換類型、原始對象和目標對象
功能實現
接口定義
數據轉換接口,裏面有一個convert轉換的方法,需要傳入原始對象,和返回對象的類型,直接返回目標對象,便於代碼規範化話和代碼隔離。
public interface Assembler<R,T> {
public T convert(R original,Class<T> targetType);
}
工廠類定義
AssemblerFactory是一個單例工廠,通過getAssembler傳入的類型,獲取目標轉換對象的實例,ReflectionUtils爲Spring-core裏面的反射方法;execute執行實現類連的轉換方法,需要傳入原始對象和目標對象。
public class AssemblerFactory {
private static AssemblerFactory INSTANCE = new AssemblerFactory();
private AssemblerFactory(){}
public static AssemblerFactory getInstance(){
return INSTANCE;
}
private Assembler getAssembler(Class type){
Assembler assembler = null;
try {
assembler = (Assembler) ReflectionUtils.accessibleConstructor(type, new Class[0]).newInstance(new Object[0]);
} catch (Throwable e){
e.printStackTrace();
}
return assembler;
}
public <R,T> T execute(Class type,R original,Class<T> targetType){
Assembler assembler = getAssembler(type);
T target = (T) assembler.convert(original,targetType);
return target;
}
}
轉換類實現
這裏提供一個樣例實現,實現接口Assembler,添加返回原始對象和目標對象,實現convert業務邏輯轉換,這裏有可能要問,裏面轉換的過程是否還可以優化?可以肯定是可以的,比如用組合模式,把DTO、VO、PO裏面的字段進行封裝、用組合 接口的方式實現,但是做起來還是比較麻煩。這裏最好是建議用get/set方法轉換,不要用一些序列化工具轉換,執行效率沒有get/set高。
/**
* UserDo 轉 UserDto
*/
public class UserDoToUserDtoAssembler implements Assembler<UserDo,UserDto>{
@Override
public UserDto convert(UserDo userDo,Class<UserDto> target){
UserDto userDto = new UserDto();
userDto.setId(userDo.getId());
userDto.setName(userDo.getName());
return userDto;
}
}
源代碼:https://github.com/itrickzhang/spring-demo/tree/master/data-conversion
本文由博客一文多發平臺 OpenWrite 發佈!