博客搬遷至https://blog.wangjiegulu.comhtml
RSS訂閱:https://blog.wangjiegulu.com/feed.xmljava
原文連接:https://blog.wangjiegulu.com/2018/04/16/rapidooo-android-pojo-converter/android
Android POJO 轉換器:根據 POJO 類編譯時自動生成支持擴展互相綁定的領域對象。git
English Versiongithub
Github: https://github.com/wangjiegulu/RapidOOOexpress
咱們在領域驅動設計中常常會在不一樣層級之間傳遞數據,例如 VO
, PO
, DO
, DTO
, BO
等。Android 的開發中也常常會遇到這些狀況,好比在 Android-CleanArchitecture 的 UserModelDataMapper::transform, UserEntityDataMapper::transform 等。手工地進行拷貝轉換的過程不但繁瑣,並且錯誤的風險比較大,在新增、刪除字段時也增長了維護的成本。Dozer 能夠很好地解決這個問題,可是在 Android 上可能就不太適用了。apache
RapidOOO 能夠作到:api
UserVO
, UserBO
等),非反射。gender
在生成的 POJO(UserVO) 中擴展出一個 genderDesc
字段,而且與原來的 gender
類共存並進行雙向綁定)conversionMethodName
, inverseConversionMethodName
等方法來進行特殊的轉換,相似 Databinding
中的 @BindingMethod
。User
生成 UserDO
, 從 UserDO
生成 UserBO
, 從 UserBO
生成 UserVO
...UserBo.create(User user)
, userBo.toUser()
。繼承
.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 }
建立 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)
以上表示一個類的轉換:
User
轉換,必填。_BO
,因此生成的類名爲 User_BO
,默認使用 @OOOs
中的 suffix
。@OOO(from = Pet.class, conversion = { @OOOConversion( fieldName = "owner", targetTypeId = "user_bo_id", targetFieldName = "ownerUser", replace = true ) })
以上也表示一個類的轉換,可是能夠經過 @OOOConversion
來新增一個字段:
@OOO
指定的 id
一致;也能夠經過 targetType
來指定 Class 類型。而後編譯將會自動生成如下代碼:
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; } }
以下新建 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_BO
和 PetBO
,生成的類名爲 UserVO
和 PetVO
。
在 UserVO
中擴展了兩個字段:
@OOOConversion( fieldName = "gender", targetFieldName = "genderDesc", targetType = String.class, conversionMethodName = "conversionGender", inverseConversionMethodName = "inverseConversionGender", replace = false )
從轉換源的 gender
字段擴展出 genderDesc
(用於在 View 上進行展現),類型爲 String
,而且 replace = false
(gender
與 genderDesc
共存):
gender
轉換爲 genderDesc
。默認爲不設置。genderDesc
轉換爲 gender
。默認爲不設置。注意:
conversionMethodName
和inverseConversionMethodName
方法指定方法名字時,方法簽名必須知足如下其一:
public static [轉換目標類型] conversionXxx([轉換源字段類型] param)
public static [轉換目標類型] conversionXxx([轉換源 class 類型] param1, [轉換源字段類型] param2)
如上面gender
和genderDesc
的轉換:public static String conversionGender(UserVO userVO, Integer gender)
public static Integer inverseConversionGender(String genderDesc)
經過設置以上兩個方法,gender
和 genderDesc
兩個字段會實現互相綁定,改變其中一個字段,另外一個字段也會自動發生改變。
@OOOConversion( fieldName = "age", targetFieldName = "ageDes", targetType = String.class, conversionMethodName = "conversionAge", conversionMethodClass = AgeConversion.class, replace = true )
UserVO
中還從轉換源的 age
擴展了一個 ageDesc
屬性(替換掉 age
字段,不共存),並指定了 conversionMethodName
,可是轉換方法並不在 VOGenerator
類中,而是在 AgeConversion
類中,因此須要顯示地進行指定 conversionMethodClass
。
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
註解,並指定 acquireMethod
和 releaseMethod
兩個方法來建立和回收相應的對象便可(這裏使用了 Android Support 包中的 Pools.SimplePool
來實現對象池)。
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.