Android POJO 轉換器 —> RapidOOO

博客搬遷至https://blog.wangjiegulu.comhtml

RSS訂閱:https://blog.wangjiegulu.com/feed.xmljava

原文連接https://blog.wangjiegulu.com/2018/04/16/rapidooo-android-pojo-converter/android

RapidOOO

Android POJO 轉換器:根據 POJO 類編譯時自動生成支持擴展互相綁定的領域對象。git

English Versiongithub

Github: https://github.com/wangjiegulu/RapidOOOexpress

爲何使用 RapidOOO?

咱們在領域驅動設計中常常會在不一樣層級之間傳遞數據,例如 VO, PO, DO, DTO, BO等。Android 的開發中也常常會遇到這些狀況,好比在 Android-CleanArchitectureUserModelDataMapper::transform, UserEntityDataMapper::transform 等。手工地進行拷貝轉換的過程不但繁瑣,並且錯誤的風險比較大,在新增、刪除字段時也增長了維護的成本。Dozer 能夠很好地解決這個問題,可是在 Android 上可能就不太適用了。apache

RapidOOO 能夠作到:api

  1. 在編譯時針對指定的初始 POJO,能夠自動生成 Java 類(好比 UserVO, UserBO 等),非反射。
  2. 能夠在生成的 POJO 類中增長配置,添加新的字段(好比經過 User 中的 gender 在生成的 POJO(UserVO) 中擴展出一個 genderDesc 字段,而且與原來的 gender 類共存並進行雙向綁定)
  3. 字段進行轉換時能夠經過指定 conversionMethodName, inverseConversionMethodName 等方法來進行特殊的轉換,相似 Databinding 中的 @BindingMethod
  4. 鏈式的 POJO 生成,如從 User 生成 UserDO, 從 UserDO 生成 UserBO, 從 UserBO 生成 UserVO...
  5. 生成類中自動生成轉換方法 UserBo.create(User user), userBo.toUser()
  6. 支持 POJO 繼承.
  7. 支持對象池(好比 android.support.v4.util.Pools)。

怎麼使用?

Gradle Check Newest Versionapp

implementation "com.github.wangjiegulu:rapidooo-api:x.x.x"
annotationProcessor "com.github.wangjiegulu:rapidooo-compiler:x.x.x"

如下經過兩個例子來講明:less

User POJO:

public class User implements Serializable {
    private Long userId;
    private String username;
    private String nickname;
    private Integer age;
    private Integer gender;
    // getter setter
}

Pet POJO:

public class Pet {
    private Long petId;
    private String petName;
    private boolean isCat;
    private boolean delete;
    private Boolean isDog;
    private Boolean clear;
    private User owner;
    // getter  settter
}

POJO 轉換爲 BO

建立 BOGenerator 類,配置如下註解:

@OOOs(suffix = BOGenerator.BO_SUFFIX, ooos = {
        @OOO(id = "user_bo_id", from = User.class, suffix = BOGenerator.BO_SUFFIX_USER),
        @OOO(from = Pet.class, conversion = {
                @OOOConversion(
                        fieldName = "owner",
                        targetTypeId = "user_bo_id",
                        targetFieldName = "ownerUser",
                        replace = true
                )
        })
})
public class BOGenerator {
    public static final String BO_SUFFIX = "BO";
    public static final String BO_SUFFIX_USER = "_BO";
}

這裏使用 @OOOs 註解來進行轉換的配置,經過 @OOO 註解來顯示地指定須要轉換成哪些類。

@OOO(id = "user_bo_id", from = User.class, suffix = BOGenerator.BO_SUFFIX_USER)

以上表示一個類的轉換:

  • id:表示本地轉換的 id,能夠爲任意字符串(需惟一),默認不設置 id。
  • from:表示轉換源,從 User 轉換,必填。
  • suffix:表示生成的 POJO 類的名字後綴,這裏是 _BO,因此生成的類名爲 User_BO,默認使用 @OOOs 中的 suffix
@OOO(from = Pet.class, conversion = {
      @OOOConversion(
              fieldName = "owner",
              targetTypeId = "user_bo_id",
              targetFieldName = "ownerUser",
              replace = true
      )
})

以上也表示一個類的轉換,可是能夠經過 @OOOConversion 來新增一個字段:

  • fieldName:指定新的字段是從轉換源 POJO 的哪一個字段派生出來的
  • targetTypeId:用來指定新的字段的類型id,須要與其它的 @OOO 指定的 id 一致;也能夠經過 targetType 來指定 Class 類型。
  • targetFieldName:指定新字段的名字,能夠任意。
  • replace:新的字段是否替換原來的字段(fieldName),若是 false,則共存。

而後編譯將會自動生成如下代碼:

public class User_BO implements Serializable {
  private Long userId;
  private String username;
  private String nickname;
  private Integer age;
  private Integer gender;

  // getter setter

  public static User_BO create(User user) {
    User_BO user_BO = new User_BO();
    user_BO.userId = user.getUserId();
    user_BO.username = user.getUsername();
    user_BO.nickname = user.getNickname();
    user_BO.age = user.getAge();
    user_BO.gender = user.getGender();
    return user_BO;
  }

  public User toUser() {
    User user = new User();
    user.setUserId(userId);
    user.setUsername(username);
    user.setNickname(nickname);
    user.setAge(age);
    user.setGender(gender);
    return user;
  }

}
public class PetBO {
  private Long petId;
  private String petName;
  private boolean isCat;
  private boolean delete;
  private Boolean isDog;
  private Boolean clear;
  private User_BO ownerUser;

  // getter setter

  public static PetBO create(Pet pet) {
    PetBO petBO = new PetBO();
    petBO.petId = pet.getPetId();
    petBO.petName = pet.getPetName();
    petBO.isCat = pet.isCat();
    petBO.delete = pet.isDelete();
    petBO.isDog = pet.getDog();
    petBO.clear = pet.getClear();
    petBO.ownerUser = User_BO.create(pet.getOwner());
    return petBO;
  }

  public Pet toPet() {
    Pet pet = new Pet();
    pet.setPetId(petId);
    pet.setPetName(petName);
    pet.setCat(isCat);
    pet.setDelete(delete);
    pet.setDog(isDog);
    pet.setClear(clear);
    pet.setOwner(ownerUser.toUser());
    return pet;
  }
}

BO 轉換爲 VO

以下新建 VOGenerator:

@OOOs(suffix = VOGenerator.VO_SUFFIX, fromSuffix = BOGenerator.BO_SUFFIX, ooosPackages = {
        VOGenerator.PACKAGE_BO
}, ooos = {
        @OOO(id = "user_vo_id", from = User_BO.class),
        @OOO(from = User_BO.class/*, suffix = VOGenerator.VO_SUFFIX_USER*/,
                fromSuffix = BOGenerator.BO_SUFFIX_USER,
                conversion = {
                        @OOOConversion(
                                fieldName = "gender",
                                targetFieldName = "genderDesc",
                                targetType = String.class,
                                conversionMethodName = "conversionGender",
                                inverseConversionMethodName = "inverseConversionGender",
                                replace = false
                        ),
                        @OOOConversion(
                                fieldName = "age",
                                targetFieldName = "ageDes",
                                targetType = String.class,
                                conversionMethodName = "conversionAge",
                                conversionMethodClass = AgeConversion.class,
                                replace = true
                        )
                }
        ),
        @OOO(from = PetBO.class,
                conversion = {
                        @OOOConversion(
                                fieldName = "ownerUser",
                                targetFieldName = "ownerUser",
                                targetTypeId = "user_vo_id",
                                replace = true
                        )
                }
        )
})
public class VOGenerator {
    public static final String VO_SUFFIX = "VO";
    //    public static final String VO_SUFFIX_USER = "_VO";
    public static final String PACKAGE_BO = "com.wangjiegulu.rapidooo.depmodule.bll.xbo";

    public static String conversionGender(Integer gender) {
        if (null == gender) {
            return "unknown";
        }
        switch (gender) {
            case 0:
                return "female";
            case 1:
                return "male";
            default:
                return "unknown";
        }
    }

    public static Integer inverseConversionGender(String genderDesc) {
        switch (genderDesc) {
            case "male":
                return 1;
            case "female":
                return 0;
            default:
                return -1;
        }
    }

}

仍是經過 @OOOs 註解來指定要生成的類,但這裏使用了 ooosPackages 來指定哪些包下面的類須要進行轉換。

轉換源爲上面生成的:User_BOPetBO,生成的類名爲 UserVOPetVO

UserVO 中擴展了兩個字段:

@OOOConversion(
       fieldName = "gender",
       targetFieldName = "genderDesc",
       targetType = String.class,
       conversionMethodName = "conversionGender",
       inverseConversionMethodName = "inverseConversionGender",
       replace = false
)

從轉換源的 gender 字段擴展出 genderDesc (用於在 View 上進行展現),類型爲 String ,而且 replace = falsegendergenderDesc 共存):

  • conversionMethodName:指定轉換方法,從 gender 轉換爲 genderDesc。默認爲不設置。
  • inverseConversionMethodName:指定逆轉換方法,從 genderDesc 轉換爲 gender。默認爲不設置。

注意:conversionMethodNameinverseConversionMethodName 方法指定方法名字時,方法簽名必須知足如下其一:

  • public static [轉換目標類型] conversionXxx([轉換源字段類型] param)
  • public static [轉換目標類型] conversionXxx([轉換源 class 類型] param1, [轉換源字段類型] param2)
    如上面 gendergenderDesc 的轉換:
  • public static String conversionGender(UserVO userVO, Integer gender)
  • public static Integer inverseConversionGender(String genderDesc)

經過設置以上兩個方法,gendergenderDesc 兩個字段會實現互相綁定,改變其中一個字段,另外一個字段也會自動發生改變。

@OOOConversion(
       fieldName = "age",
       targetFieldName = "ageDes",
       targetType = String.class,
       conversionMethodName = "conversionAge",
       conversionMethodClass = AgeConversion.class,
       replace = true
)

UserVO 中還從轉換源的 age 擴展了一個 ageDesc 屬性(替換掉 age 字段,不共存),並指定了 conversionMethodName,可是轉換方法並不在 VOGenerator 類中,而是在 AgeConversion 類中,因此須要顯示地進行指定 conversionMethodClass

  • conversionMethodClass:轉換方法所在的 Class,默認不設置則表示在當前的 Generator 類中。

另外 PetVO 擴展了一個 ownerUser

最後編譯生成的代碼以下:

public class UserVO implements Serializable {
  private Long userId;
  private String username;
  private String nickname;
  private String ageDes;
  private Integer gender;
  private String genderDesc;

  // getter setter

  public void setGender(Integer gender) {
    this.gender = gender;
    this.genderDesc = VOGenerator.conversionGender(gender);
  }

  public void setGenderDesc(String genderDesc) {
    this.genderDesc = genderDesc;
    this.gender = VOGenerator.inverseConversionGender(genderDesc);
  }

  public static UserVO create(User_BO user_BO) {
    UserVO userVO = new UserVO();
    userVO.userId = user_BO.getUserId();
    userVO.username = user_BO.getUsername();
    userVO.nickname = user_BO.getNickname();
    userVO.ageDes = AgeConversion.conversionAge(user_BO.getAge());
    userVO.gender = user_BO.getGender();
    userVO.genderDesc = VOGenerator.conversionGender(user_BO.getGender());
    return userVO;
  }

  public User_BO toUser_BO() {
    User_BO user_BO = new User_BO();
    user_BO.setUserId(userId);
    user_BO.setUsername(username);
    user_BO.setNickname(nickname);
    // Loss field:age, recommend to use `inverseConversionMethodName`.
    user_BO.setGender(gender);
    return user_BO;
  }
}

注意:以上 User_BO,因爲 age 屬性是 replace,而且只設置了 conversionMethodName,並無設置 inverseConversionMethodName,因此在 toUser_BO() 方法進行逆轉換時會丟失 age 屬性,因此推薦使用 inverseConversionMethodName

public class PetVO {
  private Long petId;
  private String petName;
  private boolean isCat;
  private boolean delete;
  private Boolean isDog;
  private Boolean clear;
  private UserVO ownerUser;

  // getter setter

  public static PetVO create(PetBO petBO) {
    PetVO petVO = new PetVO();
    petVO.petId = petBO.getPetId();
    petVO.petName = petBO.getPetName();
    petVO.isCat = petBO.isCat();
    petVO.delete = petBO.isDelete();
    petVO.isDog = petBO.getDog();
    petVO.clear = petBO.getClear();
    petVO.ownerUser = UserVO.create(petBO.getOwnerUser());
    return petVO;
  }

  public PetBO toPetBO() {
    PetBO petBO = new PetBO();
    petBO.setPetId(petId);
    petBO.setPetName(petName);
    petBO.setCat(isCat);
    petBO.setDelete(delete);
    petBO.setDog(isDog);
    petBO.setClear(clear);
    petBO.setOwnerUser(ownerUser.toUser_BO());
    return petBO;
  }
}

對象池的使用

@OOOs(suffix = "BO", ooos = {
        @OOO(from = Pet.class, pool = @OOOPool(
            acquireMethod = "acquirePetBO", 
            releaseMethod = "releasePetBO"
        ))
})
public class ObjectPoolBOGenerator {

    private static Pools.Pool<PetBO> petBOPool = new Pools.SimplePool<>(3);
    
    public static PetBO acquirePetBO() {
        PetBO petBO = petBOPool.acquire();
        return null == petBO ? new PetBO() : petBO;
    }

    public static void releasePetBO(PetBO petBO) {
        petBOPool.release(petBO);
    }
}

如上代碼,經過添加 @OOOPool 註解,並指定 acquireMethodreleaseMethod 兩個方法來建立和回收相應的對象便可(這裏使用了 Android Support 包中的 Pools.SimplePool 來實現對象池)。

License

Copyright 2018 Wang Jie

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
相關文章
相關標籤/搜索