項目中頻繁的數據轉換我們怎麼優化

我們先來看一張數據扭轉的圖,這個是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 發佈!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章