Bean设计

Bean设计

  • VO(value object):用于前端展示使用(例如放置到JSP中解析或者给前端传递数据)

  • DTO(data transfer object):用于接口互相调用返回,数据传输(例如很多接口调用返回值或消息队列内容);

  • PO(persistence object):用于持久化时(例如保存到数据库或者缓存);

Java各种对象(PO,BO,VO,DTO,POJO,DAO,Entity,JavaBean,JavaBeans)的区分arrow-up-right

VO、BO、DTO相互转换

通过硬编码实现Bean之间相互转化,简单但是费力。要是能对其封装一下就好了。

思路一:封装一个工具类实现相互转换

工具类

com.moluo.moapi.common.util.ConvertUtil

package com.moluo.moapi.common.util;

import com.moluo.moapi.common.bean.MoPage;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;

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

/**
 * 转换工具类
 * <p>
 * 用于实现VO、BO、DTO等对象之间的相互转换
 */
public class ConvertUtil {

    /**
     * 源类型对象转换为目标类型对象
     *
     * @param src   源类型对象
     * @param clazz 目标类
     * @param <S>   源类
     * @param <T>   目标类
     * @return 目标类型对象
     */
    public static <S, T> T convertTo(S src, Class<T> clazz) {
        T result = null;
        try {
            result = clazz.newInstance();
            BeanUtils.copyProperties(src, result);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 源类型列表转换为目标类型列表
     *
     * @param srcList 源类型列表
     * @param clazz   目标类
     * @param <S>     源类
     * @param <T>     目标类
     * @return 目标类型列表
     */
    public static <S, T> List<T> convertTo(List<S> srcList, Class<T> clazz) {
        List<T> result = new ArrayList<>(srcList.size());
        for (Object srcItem : srcList) {
            try {
                T destItem = clazz.newInstance();
                BeanUtils.copyProperties(srcItem, destItem);
                result.add(destItem);
            } catch (InstantiationException | IllegalAccessException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 源类型Page转换为目标类型MoPage
     *
     * @param srcPage 源类型Page
     * @param clazz   目标类
     * @param <S>     源类
     * @param <T>     目标类
     * @return 目标类型MoPage
     */
    public static <S, T> MoPage<T> convertTo(Page<S> srcPage, Class<T> clazz) {
        MoPage<T> result = new MoPage<>();
        result.setPageNo(srcPage.getNumber() + 1);
        result.setPageSize(srcPage.getPageable().getPageSize());
        result.setTotalCount(srcPage.getTotalElements());
        List<T> list = new ArrayList<>();
        for (Object srcItem : srcPage.getContent()) {
            try {
                T destItem = clazz.newInstance();
                BeanUtils.copyProperties(srcItem, destItem);
                list.add(destItem);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        result.setData(list);
        return result;
    }

    /**
     * 源类型Page转换为同类型MoPage
     *
     * @param srcPage 源类型Page
     * @param <T>     源类型
     * @return 同类型MoPage
     */
    public static <T> MoPage<T> convertTo(Page<T> srcPage) {
        MoPage<T> result = new MoPage<>();
        result.setPageNo(srcPage.getNumber() + 1);
        result.setPageSize(srcPage.getPageable().getPageSize());
        result.setTotalCount(srcPage.getTotalElements());
        result.setData(srcPage.getContent());
        return result;
    }

}

使用方式

意见

LinkVO linkVo = ConvertUtil.convertTo(link, LinkVO.class);的方式看起来已经挺简洁了,但或许它还可以更简洁,比如省略LinkVO.class,达到LinkVO linkVo = ConvertUtil.convertTo(link);的效果

思路二:编写一个父类实现相互转换

父类

com.moluo.moapi.common.BaseController

com.moluo.moapi.common.BaseVO

com.moluo.moapi.common.BaseParam

使用方式

com.moluo.moapi.life.controller.LifeEventController

意见

通过父类对转换逻辑的封装,使用户不必关心VO、BO具体如何转换。只需要记住通过convert()方式就可以实现转换。但各个bean需要继承指定的基础Bean,例如VO需要继承BaseVO,会令人感到束缚。同时还需要实现getVoClass()getParamClass()getEntityClass()方法也令人不悦。

思路三:编写一个接口实现相互转换

接口

com.moluo.moapi.common.Controller

com.moluo.moapi.common.BaseVO

com.moluo.moapi.common.BaseParam

使用方式

意见

相比手写转换方法,通过实现Controller接口,可以快速搭建转换方法架构(通过ALT+ENTER让IDE自动生成)。但仅有框架,开发者仍需要编写具体实现代码。

讨论

小编最希望实现的效果是:只继承一个父类,无须重写任何方法,即可通过统一的convert()方法实现各Bean之间的转换。但由于在父类中无法直接获取到泛型的Class,导致小编不得不将获取Class的逻辑转移到子类中,最终形成令人不甚满意的思路二,令人十分可惜。

记录以上三种思路,希望未来能找到更优雅的实现方式。

Last updated