DDD防腐层的设计

防腐层是在DDD是占有非常重要的作用,具体分析可以见这篇文章,这里就不细说了,但是在Java代码中防腐层应该怎么设计呢?目前比较好的有以下几种。

  • 通过cglib的BeanCopier。
  • 通过Spring的BeanUtils。
  • 通过mapstruct。
  • 硬编码。

下面分别通过例子来看下优缺点,最后做个选择,现在有两个对象,一个是OriginOrder,一个是TargetOrder,需要把OriginOrder转化为TargetOrder。

cglib的BeanCopier

代码如下:

public class CglibBeanCopierConvertor {
    private static final BeanCopier copier = BeanCopier.create(OriginalOrder.class, TargetOrder.class, false);

    public static TargetOrder convert(OriginalOrder originalOrder) {
        TargetOrder targetOrder = new TargetOrder();
        copier.copy(originalOrder, targetOrder, null);
        return targetOrder;
    }
}

cglib的实现原理见文章

Spring的BeanUtils

代码如下:

public class SpringBeanUtilConvertor {
    public static TargetOrder convert(OriginalOrder originalOrder){
        TargetOrder targetOrder = new TargetOrder();
        BeanUtils.copyProperties(originalOrder, targetOrder);
        return targetOrder;
    }
}

原理如下: https://blog.csdn.net/sosozha/article/details/80710174

mapstruct

代码如下:

@Mapper
public interface MapStructConvertor {
    MapStructConvertor CONVERTOR = Mappers.getMapper(MapStructConvertor.class);
    @Mappings({})
    TargetOrder convert(OriginalOrder originalOrder);
}

原理如下: https://juejin.cn/post/6844904199755415559

硬编码的方式

public class HardCodeConvertor {
    public static TargetOrder convert(OriginalOrder originOrder){
        if (originOrder == null){
            return null;
        }
        TargetOrder targetOrder = new TargetOrder();
        targetOrder.setCode(originOrder.getCode());
        targetOrder.setBatchCode(originOrder.getBatchCode());
        targetOrder.setType(originOrder.getType());
        targetOrder.setOrderUserId(originOrder.getOrderUserId());
        targetOrder.setOrderDate(originOrder.getOrderDate());
        targetOrder.setPurchaseDate(originOrder.getPurchaseDate());
        targetOrder.setOrderComment(originOrder.getOrderComment());
        targetOrder.setProjectId(originOrder.getProjectId());
        targetOrder.setProjectType(originOrder.getProjectType());
        targetOrder.setProjectPhase(originOrder.getProjectPhase());
        targetOrder.setProjectOperatorVersion(originOrder.getProjectOperatorVersion());
        targetOrder.setOrganizationId(originOrder.getOrganizationId());
        targetOrder.setSupplierId(originOrder.getSupplierId());
        targetOrder.setWarehouseId(originOrder.getWarehouseId());
        targetOrder.setWarehouseType(originOrder.getWarehouseType());
        targetOrder.setWarehousePickupCode(originOrder.getWarehousePickupCode());
        targetOrder.setAllowMultipleDelivery(originOrder.getAllowMultipleDelivery());
        targetOrder.setSkuAmount(originOrder.getSkuAmount());
        targetOrder.setOriginalTotalAmount(originOrder.getOriginalTotalAmount());
        targetOrder.setCompositeAmount(originOrder.getCompositeAmount());
        targetOrder.setOriginalCompositeAmount(originOrder.getOriginalCompositeAmount());
        targetOrder.setTotalAmount(originOrder.getTotalAmount());
        targetOrder.setAuditStatus(originOrder.getAuditStatus());
        targetOrder.setAuditDate(originOrder.getAuditDate());
        targetOrder.setAuditVersion(originOrder.getAuditVersion());
        targetOrder.setStatus(originOrder.getStatus());
        targetOrder.setDeliveryFlag(originOrder.getDeliveryFlag());
        targetOrder.setSupplierConfirmDate(originOrder.getSupplierConfirmDate());
        targetOrder.setSupplierComment(originOrder.getSupplierComment());
        targetOrder.setCompleteDate(originOrder.getCompleteDate());
        targetOrder.setInvalidUserId(originOrder.getInvalidUserId());
        targetOrder.setInvalidDate(originOrder.getInvalidDate());
        targetOrder.setInvalidReasonCode(originOrder.getInvalidReasonCode());
        targetOrder.setInvalidComment(originOrder.getInvalidComment());
        targetOrder.setCancelDate(originOrder.getCancelDate());
        targetOrder.setCancelReasonCode(originOrder.getCancelReasonCode());
        targetOrder.setCancelComment(originOrder.getCancelComment());
        targetOrder.setAbort(originOrder.getAbort());
        targetOrder.setAbortDate(originOrder.getAbortDate());
        targetOrder.setAbortComment(originOrder.getAbortComment());
        targetOrder.setFreezeStatus(originOrder.getFreezeStatus());
        targetOrder.setParentId(originOrder.getParentId());
        targetOrder.setSplited(originOrder.getSplited());
        targetOrder.setItemTagId(originOrder.getItemTagId());
        targetOrder.setItemTagName(originOrder.getItemTagName());
        targetOrder.setRegionId(originOrder.getRegionId());
        targetOrder.setOrderCategory(originOrder.getOrderCategory());
        targetOrder.setOldAuditStatus(originOrder.getOldAuditStatus());
        targetOrder.setBidding(originOrder.getBidding());
        targetOrder.setJcReceiving(originOrder.getJcReceiving());
        targetOrder.setDeliveryMethod(originOrder.getDeliveryMethod());
        targetOrder.setDealCorporation(originOrder.getDealCorporation());
        targetOrder.setEvaluateStatus(originOrder.getEvaluateStatus());
        targetOrder.setTenantId(originOrder.getTenantId());
        targetOrder.setTenantSettleType(originOrder.getTenantSettleType());
        targetOrder.setBizSource(originOrder.getBizSource());
        targetOrder.setFreightAmount(originOrder.getFreightAmount());
        targetOrder.setFreightDiscountAmount(originOrder.getFreightDiscountAmount());
        targetOrder.setVersion(originOrder.getVersion());
        targetOrder.setAcceptance(originOrder.getAcceptance());
        targetOrder.setAcceptanceQuantity(originOrder.getAcceptanceQuantity());
        targetOrder.setSubmitType(originOrder.getSubmitType());
        targetOrder.setChannel(originOrder.getChannel());
        return targetOrder;
    }

四种方式性能分析

少量调用次数下

1次调用下:

threadHardCode consume time: 3
threadMapStruct consume time: 4
threadCglib consume time: 95
threadSpringBean consume time: 94

10次调用下:

threadHardCode consume time: 6
threadMapStruct consume time: 6
threadCglib consume time: 96
threadSpringBean consume time: 95

10000次调用下:

threadMapStruct consume time: 125
threadHardCode consume time: 131
threadCglib consume time: 221
threadSpringBean consume time: 220

1000000次调用下:

threadMapStruct consume time: 424
threadHardCode consume time: 435
threadCglib consume time: 452
threadSpringBean consume time: 456

从耗时来看,调用次数少的情况下,硬编码和mapStruct很接近,而cglib和BeanUtils是耗时很长,原因是硬编码和mapStruct在运行期直接执行的赋值操作,而cglib和BeanUtils还需要通过反射生成Convetor的字节码,但是生成之后会缓存起来,所以在调用次数达到百万以上时,耗时是很接近的。

四种方式优缺点分析

方式 优点 缺点 解决缺点的方案
cglib的BeanCopier 简单好用。 1. 少量调用下性能低。 2. ide无法跟踪到调用链路。3. 不同类型的字段无法赋值 4.字面上无法搜索到关键词 暂无解法
Spring的BeanUtils 简单好用。 1. 少量调用下性能低。 2. ide无法跟踪到调用链路。3. 不同类型的字段无法赋值 4.字面上无法搜索到关键词 暂无解法
mapstruct 性能高,相对简单好用 1. 字面上无法搜索到关键词。2. 编译之前无法看到调用链路,编译之后ide可以跟踪到调用链路 需要看调用链路时编译一次,但是代码有问题时无法编译通过
硬编码 性能高 手动编写,效率低下 可以通过工具或者ide插件来生成

从优缺点来看,我推荐硬编码+代码生成器,理由如下:

  1. ide无法跟踪到调用链路这个是无法接受的,因为但凡出了问题,比如变量值不正确等,排查问题解决问题是最重要的。
  2. 不同类型的字段无法赋值,这个在高质量的系统里是无法容忍的,比如DO对象的属性是Byte,转化后的属性枚举,这个cglib的BeanCopier和Spring的BeanUtils是无法接受的。
  3. 硬编码的手动编写可以通过工具、脚本或者ide的插件来生成,只需要一次生成,后面只需要改造就行了。

https://blog.csdn.net/vbnetfun/article/details/118632844
https://blog.csdn.net/loushuiyifan/article/details/82461955

你可能感兴趣的:(DDD,Java单元测试,DDD,防腐层,架构,Java)