设计模式之:模版模式

在我们平时的编码工作中,经常会有一些流程步骤基本相同,只是中间有部分细节不同的业务流程,比如我们自己要实现一个基于redis缓存的模版方法,在redis中存在指定值时,则返回redis中缓存的数据;如果redis中不存在,则查询db,并把查询所得缓存起来,并返回查询所得。这个例子就是用模版模式再好不过了,直接上代码:

1.定义模版类

@Component
public abstract class RedisCacheTemplate {

	@Autowired
	private RedisTemplate<String, Object> redisTemplate;

	/**
	 * 查询单个对象
	 * 
	 * @param key         redis缓存key
	 * @param targetClass 返回数据对象类型
	 * @return 缓存值
	 */
	@SuppressWarnings("unchecked")
	public final <T> T findFromCache(String key, Class<T> targetClass) {
		T target = BeanUtils.instantiateClass(targetClass);
		// 1.判断redis中有无数据,有数据,直接返回
		if (redisTemplate.hasKey(key)) {
			Object result = redisTemplate.boundValueOps(key).get();
			target = (T) result;
		} else {
			if (isSearchDB()) {// 钩子方法,判断是否要查询DB,由业务方实现
				synchronized (RedisCacheTemplate.class) {// 加锁,防止并发查询或者操作DB
					if (!redisTemplate.hasKey(key)) {
						// 2.redis中无数据,则查找数据库
						target = searchFromDB();
						// 3.将数据库查询出的数据,放在redis中
						if (timeOut() == -1L) {// 由业务方判断缓存是否需要超时时间
                                // 其实这里还有一个可以优化的地方,就是数据库没有查询到,
                                // 我们可以在缓存中保存一个特殊的值,这样,如果数据中不存在满足条件的数据,也不会每次查询DB了
							redisTemplate.boundValueOps(key).set(target);
						} else {
							redisTemplate.boundValueOps(key).set(target, timeOut(), timeOutTimeUnit());
						}
					}
				}
			} else {
                    // 缓存没有命中,并不查询DB,直接返回null
				return null;
			}

		}
		// 返回查询数据
		return target;
	}

	/**
	 * 钩子方式,供用户实现,判断是否要查找数据库,默认不查DB
	 * 
	 * @return false
	 */
	protected boolean isSearchDB() {
		return false;
	};

	/**
	 * 钩子方法,设置过期时间
	 * 
	 * @return 默认-1L:不过期
	 */
	protected long timeOut() {
		return -1L;
	};

	/**
	 * 设置过期时间单位
	 * 
	 * @return 默认TimeUnit.HOURS
	 */
	protected TimeUnit timeOutTimeUnit() {
		return TimeUnit.HOURS;
	};

	/**
	 * 查看DB逻辑,供用户实现
	 * 
	 * @return DB查找结果
	 */
	protected abstract <T> T searchFromDB();

}

通常,因为模版方式需要一些钩子方法,或者其他需要业务方自己实现的具体方法,我们一般将模版类定义为抽象类,提供供业务实现的抽象方法,或者定义一个protect的方法,返回默认值,必要时由业务方重写该方法。

 

2.业务方定制具体细节

@Component
public class ServiceBootRedisCacheTemplate extends RedisCacheTemplate {

	@Autowired
	private TaskService taskService;
        //业务方确认是否需要查询DB
	@Override
	protected boolean isSearchDB() {
		return true;
	}

        //若需要查询DB,则实现查询DB的逻辑
	@SuppressWarnings("unchecked")
	@Override
	protected TaskResponseDTO searchFromDB() {
		TaskResponseDTO result = taskService.findOneById(1L);
		return result;
	}

}

3.使用我们的缓存模版

	@RequestMapping("/test")
	public String test() {
		TaskResponseDTO res = cacheTempalte.findFromCache("lichangwu", TaskResponseDTO.class);
		System.out.println(res);
		System.out.println(userService.get(1L));
		return "0000";
	}

一个简单的模版模式就实现了,是不是很简单!

 

补充2点:

1.如果查询DB需要参数,也很简单,提供的抽象方法带参数即可。也可以在我们具体实现的ServiceBootRedisCacheTemplate 中定义需要的参数变量,提供set方法,也可实现该功能

2.我们第一次查询缓存,如果没有命中的话,我们就会查询数据库,如果查询数据库也没有满足条件的数据,即db查询返回null,如果不设置默认值在缓冲中,则每次这样的查询都会走DB,我们的缓存模版也就没有意义了。最好的做法是,如果DB查询不到,我们在缓存中设置一个指定的默认值,业务方根据返回的值做相应的业务逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值