【版本任你发】-9-接口的默认实现

120 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

1. 背景

  话说我们的虾搞一通餐饮公司因为业务做越做大,马上就要上市了。这个时候,不停有第三方公司对接他们的业务平台。有一天,小黄的组长老陈交给她一个任务:“你把这六张表全部的内容,按照对方的格式推送到这个地址。要分页去推送不要扫全表,一页大概200条就行了。”

  小黄接到任务后陷入了沉思,她开始和小孙讨论这个重复性的操作。

2. 每个表都要分页

  小孙问小黄:“每张表都多大?全量同步行吗?”小黄无奈地说:“别那么幼稚行吗?都大概500万条,全量去同步肯定不行,只能分页同步!但是分页去扫,肯定又是很多重复代码,你能不能想想办法?”

  小孙灵机一动:“有了!这不是我前两天看的接口默认实现吗?”小黄疑惑地看着他,小孙开始敲击了代码:

  首先,我们定义一个统一的DAO层:

public interface PageableDao {

    /**
     * 统计全体数量
     *
     * @return
     */
    long countAll();

    /**
     * 若对全体内容进行分页
     * 使用此接口获取页数
     *
     * @param pageSize
     * @return
     */
    default int getPagesInAll(int pageSize) {
        long totalNumber = countAll();
        return getPages(totalNumber, pageSize);
    }

    default int getPages(long totalNumber, int pageSize) {
        int pages = (int) (totalNumber / pageSize);
        long remainder = totalNumber % pageSize;
        if (remainder != 0) {
            pages++;
        }
        return pages;
    }

}

  在这个DAO层中,我们延迟了对于统计全体数量的实现,这交由每个表去实现即可。

public interface OrderInfoDao extends PageableDao{

    /**
     * 添加一个订单信息
     *
     * @param orderInfo
     */
    void addOne(OrderInfo orderInfo);


}

  就拿设备表来说,它可以有自己的业务,通过继承来扩展这个接口即可。最后的实现类可以实现最具体的内容。

3. 更多示例

  小贺听到了他们的讨论,笑着补充说道:“你们这个东西,其实在MybatisPlus里边经常用到,我就拿com.baomidou.mybatisplus.core.mapper.BaseMapper来说吧,它就有几个默认实现。”

public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteById(T entity);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> queryWrapper);

    int deleteBatchIds(@Param("coll") Collection<?> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {
        List<T> ts = this.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(ts)) {
            if (ts.size() != 1) {
                throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records", new Object[0]);
            } else {
                return ts.get(0);
            }
        } else {
            return null;
        }
    }

    default boolean exists(Wrapper<T> queryWrapper) {
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0L;
    }

    Long selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    <P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);

    <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
}

  这个里面的selectOne就复用了selectList方法,减少了重复的代码。

总结

  本例中,我们介绍了接口的默认实现以减少重复代码。我们在实际开发过程中,可以反复思考:某几个接口是否有同样的逻辑,可以放在子类去实现。同一级的接口,是否可以提供默认的实现来调用或引用?

  关于接口的默认实现,你又有哪些好的实践呢?欢迎你在评论区和我讨论。