场景

在开发的时候经常会遇到批量取缓存的问题,例如查询商品信息

  1. 传入一个商品Id列表,查询Redis数据存在则放入返回列表
  2. 不存在的数据查找数据库,并放入Redis
  3. 上面两步数据整合返回
    伪代码为
list.ForEach(str->{
	dto = redis.get(str);
		if(dto != null){
		resultList.add(dto);
	}else{
		mustQuerySQL.add(str);
	}
})
if(CollectionUtils.isNotEmpty(mustQuerySQL)){
	querysqlList = querySQL(mustQuerySQL);
	querySqlList.ForEach(dto->{
		redis.put(dto);	
		resultList.add(dto);
	}
})
return resultList;

问题是在项目中经常使用这样的模式,有什么办法可以简化步骤呢?

模板方法模式

模板方法模型是一种行为设计模型。模板方法是一个定义在父类别的方法,在模板方法中会呼叫多个定义在父类别的其他方法,而这些方法有可能只是抽象方法并没有实作,模板方法仅决定这些抽象方法的执行顺序,这些抽象方法的实作由子类别负责,并且子类别不允许覆写模板方法。
我们可以使用模板方法模式,具体的从Redis中获取数据以及SQL中获取数据和SQL中数据放入Redis让子类进行实现。

实现

代码如下:

public abstract class RedisUtil<T>  {

    public final List<T> getList(List<String> list){
        List<T> resultList = new ArrayList<>();
        List<String> mustQuery = new ArrayList<>();
        list.forEach(str->{
            T o = queryForRedis(str);
            if(o != null){
                resultList.add(o);
            }else {
                mustQuery.add(str);
            }
        });
        if(CollectionUtils.isNotEmpty(mustQuery)) {
            List<T> sql = queryForSQL(mustQuery);
            sql.forEach(dto->{
                setForRedis(dto);
                resultList.add(dto);
            });
        }
        return resultList;
    }

    protected abstract T queryForRedis(String str);

    protected abstract List<T> queryForSQL(List<String> list);

    protected abstract void setForRedis(T t);
}

子类只需要使用匿名类即可实现,但是这样在代码中会有这样的代码

RedisUtil<String> redisUtil = new RedisUtil<String>() {
            @Override
            protected String queryForRedis(String str) {
                return null;
            }

            @Override
            protected List<String> queryForSQL(List<String> list) {
                return null;
            }

            @Override
            protected void setForRedis(String s) {

            }
        };

占据大量的代码段,看起来并不优雅,如何优雅的实现呢?

优化

上面使用了匿名类,但是这样占据代码段,当然可以新建一个实体类进行实现,但是总归是不太优雅。
我们可以使用JDK8中提供的函数式接口实现

public class RedisUtil  {
    public static <T> List<T> getList(List<String> list, Function<String,T> queryRedis, Function<List<String>,List<T>> querySQL, Consumer<T> setRedis){
        List<T> resultList = new ArrayList<>();
        List<String> mustQuery = new ArrayList<>();
        list.forEach(str->{
            T o = queryRedis.apply(str);
            if(o != null){
                resultList.add(o);
            }else {
                mustQuery.add(str);
            }
        });
        if(CollectionUtils.isNotEmpty(mustQuery)) {
            List<T> sql = querySQL.apply(mustQuery);
            sql.forEach(dto->{
                setRedis.accept(dto);
                resultList.add(dto);
            });
        }
        return resultList;
    }
}