Bean设计

Bean设计

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

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

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

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

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;
    }

}

使用方式

/**
 * 将链接入参转化为链接
 *
 * @param linkParam 链接入参
 * @return 链接
 */
private Link convert(LinkParam linkParam) {
    return ConvertUtil.convertTo(linkParam, Link.class);
}


/**
 * 将链接转化为链接VO
 *
 * @param link 链接
 * @return 链接VO
 */
private LinkVO convert(Link link) {
    return ConvertUtil.convertTo(link, LinkVO.class);
}

/**
 * 根据Page对象生成MoPage
 *
 * @param page 分页
 * @return MoPage
 */
private MoPage<LinkVO> convert(Page<Link> page) {
    return ConvertUtil.convertTo(page, LinkVO.class);
}

/**
 * 将链接列表转化为链接VO列表
 *
 * @param listLinks 链接列表
 * @return 链接VO列表
 */
private List<LinkVO> convert(List<Link> listLinks) {
    return ConvertUtil.convertTo(listLinks, LinkVO.class);
}

意见

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

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

父类

com.moluo.moapi.common.BaseController

package com.moluo.moapi.common;

import com.moluo.moapi.common.bean.MoPage;
import com.moluo.moapi.common.util.ConvertUtil;
import com.moluo.moapi.model.entity.base.BaseEntity;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;

import java.util.List;

/**
 * Controller基础类
 */
public abstract class BaseController {

    /**
     * 将Param转化为Entity
     *
     * @param param 入参
     * @param <E>   Entity类型
     * @param <P>   Param类型
     * @return Entity
     */
    protected <E extends BaseEntity, P extends BaseParam> E convert(P param) {
        E result = null;
        try {
            result = (E) getEntityClass().newInstance();
            BeanUtils.copyProperties(param, result);
        } catch (InstantiationException | IllegalAccessException ex) {
            ex.printStackTrace();
        }
        return result;
    }

    /**
     * 将Entity转化为VO
     *
     * @param entity 实体
     * @param <E>    Entity类型
     * @param <VO>   VO类型
     * @return VO
     */
    protected <E extends BaseEntity, VO extends BaseVO> VO convert(E entity) {
        VO result = null;
        try {
            result = (VO) getVoClass().newInstance();
            BeanUtils.copyProperties(entity, result);
        } catch (InstantiationException | IllegalAccessException ex) {
            ex.printStackTrace();
        }
        return result;
    }


    /**
     * 将Page<Entity>转化为MoPage<VO>
     *
     * @param srcPage 源Page
     * @param <VO>    VO类型
     * @param <E>     Entity类型
     * @return MoPage<VO>
     */
    protected <VO extends BaseVO, E extends BaseEntity> MoPage<VO> convert(Page<E> srcPage) {
        return ConvertUtil.convertTo(srcPage, getEntityClass());
    }

    /**
     * 将Entity列表转化为VO列表
     *
     * @param srcList Entity列表
     * @param <VO>    VO类型
     * @param <E>     Entity类型
     * @return VO列表
     */
    protected <VO extends BaseVO, E extends BaseEntity> List<VO> convert(List<E> srcList) {
        return ConvertUtil.convertTo(srcList, getVoClass());
    }

    /**
     * 获取VO的类型
     *
     * @return VO的类型
     */
    public abstract Class getVoClass();

    /**
     * 获取Param的类型
     *
     * @return Param的类型
     */
    public abstract Class getParamClass();

    /**
     * 获取Entity的类型
     *
     * @return Entity的类型
     */
    public abstract Class getEntityClass();


}

com.moluo.moapi.common.BaseVO

package com.moluo.moapi.common;

/**
 * VO基础类
 */
public class BaseVO {
}

com.moluo.moapi.common.BaseParam

package com.moluo.moapi.common;

/**
 * Param基础类
 */
public class BaseParam {
}

使用方式

com.moluo.moapi.life.controller.LifeEventController

package com.moluo.moapi.life.controller;

import com.moluo.moapi.common.BaseController;

public class LifeEventController extends BaseController {

    @Autowired
    private LifeEventService lifeEventService;

    ...

    public LifeVo createLifeEvent(LifeParam lifeParam) {
        LifeEvent lifeEvent = convert(lifeParam);
        return convert(lifeEventService.createLifeEvent(lifeEvent));
    }

    @Override
    public Class getVoClass() {
        return LifeVo.class;
    }

    @Override
    public Class getParamClass() {
        return LifeParam.class;
    }

    @Override
    public Class getEntityClass() {
        return LifeEvent.class;
    }
}

意见

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

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

接口

com.moluo.moapi.common.Controller

package com.moluo.moapi.common;

import com.moluo.moapi.common.bean.MoPage;
import com.moluo.moapi.model.entity.base.BaseEntity;
import org.springframework.data.domain.Page;

import java.util.List;

/**
 * Controller基础接口
 */
public interface Controller<T extends BaseParam, S extends BaseEntity, U extends BaseVO> {
    /**
     * 将Param转化为Entity
     *
     * @param t 入参
     * @return Entity
     */
    S convert(T t);

    /**
     * 将Entity转化为VO
     *
     * @param s 实体
     * @return VO
     */
    U convert(S s);

    /**
     * 将Page<Entity>转化为MoPage<VO>
     *
     * @param page 源Page
     * @return MoPage<VO>
     */
    MoPage<U> convert(Page<S> page);

    /**
     * 将Entity列表转化为VO列表
     *
     * @param list Entity列表
     * @return VO列表
     */
    List<U> convert(List<S> list);

}

com.moluo.moapi.common.BaseVO

package com.moluo.moapi.common;

/**
 * VO基础接口
 */
public interface BaseVO {
}

com.moluo.moapi.common.BaseParam

package com.moluo.moapi.common;

/**
 * Param基础接口
 */
public interface BaseParam {
}

使用方式

package com.moluo.moapi.link.controller;

/**
 * 链接(Link)表控制层
 */
public class LinkController implements Controller<LinkParam, Link, LinkVO> {

    ...

    @Override
    public Link convert(LinkParam linkParam) {
        return null;
    }

    @Override
    public LinkVO convert(Link link) {
        return null;
    }

    @Override
    public MoPage<LinkVO> convert(Page<Link> page) {
        return null;
    }

    @Override
    public List<LinkVO> convert(List<Link> list) {
        return null;
    }
}

意见

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

讨论

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

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

Last updated

Was this helpful?