Java 中常见的模型转换器

640 阅读2分钟

简介

日常工作中往往会遇到需要将传输对象 DTO 或者 Request 转换成 Model 或者 DO(entity)的场景。 DTO 为系统与外界交互的模型对象,那么肯定会有一个步骤是将 DTO 对象转化为 MO 对象或者是普通的 DO 对象,让 service 层去处理。

内容

比如创建订单操作,我这里简单列举一些字段,当用户创建订单的时候,需要传入订单号,商品名,价格等字段,后端接收到数据后,增加创建时间,主键ID,更新时间等默认字段,然后进行保存数据库的操作。

   
    Order order = new Order();       
    order.setOrderId(request.getOrderId());     
    order.setName(request.getName());
    order.setPriece(request.getPriece());

我们看一下上述这块转换代码,从逻辑上是没有问题的,但是这种写法如果放在业务逻辑代码中,势必会影响代码的整体阅读,那么为什么不选择把这部分代码抽成一个转换类呢。 通过转换类提供一个转换的过程,这样不仅让业务层显得更加简洁,而且也不会在业务层中显示具体的逻辑实现。

Order order = orderModelConverter.toDestinationModel(request);

而上述所说的创建时间,主键ID,更新时间等默认字段其实也可以通过一个抽象的模型转换器来实现,而具体的字段可以通过继承父类转换器类和实现模型转换器接口重写相应的转换方法。

/**
 * 模型转换器
 *
 * @author dinge
 * @version ModelConverter:ModelConverter.java, v0.1 2022年 05月 16日 10:15 dinge Exp $
 */
public interface ModelConverter<SourceModel, DestinationModel> {

    /**
     * 转换成源模型
     *
     * @param destinationModel 目标模型
     * @return
     */
    SourceModel toSourceModel(DestinationModel destinationModel);

    /**
     * 转换成源模型列表
     *
     * @param destinationModels 目标模型列表
     * @return
     */
    List<SourceModel> toSourceModels(List<DestinationModel> destinationModels);

    /**
     * 转换成目标模型
     *
     * @param sourceModel 源模型
     * @return
     */
    DestinationModel toDestinationModel(SourceModel sourceModel);

    /**
     * 转换成目标模型列表
     *
     * @param sourceModels 源模型列表
     * @return
     */
    List<DestinationModel> toDestinationModels(List<SourceModel> sourceModels);
}

以及转换器接口的实现类

import org.apache.commons.collections.CollectionUtils;
import com.google.common.collect.Lists;

import java.util.ArrayList;
import java.util.List;

/**
 * 抽象的模型转换器实现
 *
 * @author dinge
 * @version AbstractModelConverter:AbstractModelConverter.java, v0.1 2022年 05月 16日 13:42 dinge Exp $
 */
public abstract class AbstractModelConverter<SourceModel, DestinationModel> implements ModelConverter<SourceModel, DestinationModel> {

    @Override
    public List<SourceModel> toSourceModels(List<DestinationModel> destinationModels) {

        if(CollectionUtils.isEmpty(destinationModels)) {
            return Lists.newArrayList();
        }

        List<SourceModel> sourceModels = new ArrayList<>(destinationModels.size());

        for (DestinationModel destinationModel : destinationModels) {
            SourceModel sourceModel = toSourceModel(destinationModel);

            if (sourceModel == null) {
                continue;
            }
            sourceModels.add(sourceModel);
        }
        return sourceModels;
    }

    @Override
    public List<DestinationModel> toDestinationModels(List<SourceModel> sourceModels) {

        if(CollectionUtils.isEmpty(sourceModels)) {
            return Lists.newArrayList();
        }

        List<DestinationModel> destinationModels = new ArrayList<>(sourceModels.size());

        for (SourceModel sourceModel : sourceModels) {
            DestinationModel destinationModel = toDestinationModel(sourceModel);

            if (destinationModel == null) {
                continue;
            }
            destinationModels.add(destinationModel);
        }
        return destinationModels;
    }
}

这样重构后,我们发现业务层代码会变得简洁规范。既然有request转model的转换器,是不是应该也需要一个合适的DO 转换器呢。

/**
 * 抽象的DO转换器实现
 *
 * @author dinge
 * @version AbstractDOConverter:AbstractDOConverter.java, v0.1 2022年 05月 16日 14:04 dinge Exp $
 */
public abstract class AbstractDOConverter<SourceModel, DestinationModel>
        extends AbstractModelConverter<SourceModel, DestinationModel> implements ModelConverter<SourceModel, DestinationModel> {

    @Override
    public SourceModel toSourceModel(DestinationModel destinationModel) {

        if (destinationModel == null) {
            return null;
        }

        SourceModel sourceModel = toSourceModel0(destinationModel);
        if (sourceModel == null) {
            return null;
        }

        if (sourceModel instanceof BaseStdCoreModel && destinationModel instanceof BaseDO) {
            ((BaseStdCoreModel) sourceModel).setId(((BaseDO) destinationModel).getId());
        }
        return sourceModel;
    }

    @Override
    public DestinationModel toDestinationModel(SourceModel sourceModel) {

        if (sourceModel == null) {
            return null;
        }

        DestinationModel destinationModel = toDestinationModel0(sourceModel);
        if (destinationModel == null) {
            return null;
        }

        if (sourceModel instanceof BaseStdCoreModel && sourceModel instanceof BaseDO) {
            ((BaseStdCoreModel) destinationModel).setId(((BaseDO) sourceModel).getId());
        }
        return destinationModel;
    }

    /**
     * 转换成源模型
     *
     * @param destinationModel 目标模型
     * @return
     */
    protected abstract SourceModel toSourceModel0(DestinationModel destinationModel);

    /**
     * 转换成目标模型
     *
     * @param sourceModel 源模型
     * @return
     */
    protected abstract DestinationModel toDestinationModel0(SourceModel sourceModel);
}

此处的 BaseStdCoreModel 和 BaseDO 即是公共的模型类和实体类,其中可以封装一些公共字段,例如ID,创建时间,更新时间,拓展信息,备注之类的字段。 在封装这些字段的时候,也试试使用 lombok插件吧,这样会省略很多的的Getter 和 Setter方法,当然不想用lombok 插件的话就还是使用idea的快捷生成,看个人爱好吧。

总结

通过转换器我们可以避免在业务代码中暴露太多的实体信息,也规范简洁了我们的代码,提高了代码的可读性,何乐而不为。

今日分享到此,如果有不同意见,还请不吝赐教。