模版方法模式
背景介绍
某业务功能需要轮询指定的redis集群,pop出各个redis节点指定队列的信息,并进行处理。对接此业务的不同产品,对pop出的信息进行不同的处理流程。
每个产品服务业务流程
1. 同一队列,散列在redis集群各个节点,为了及时处理消息,构造线程池处理不同redis节点
2. 针对单个线程,调用jedis接口(这里需构造jedis对象池)pop出指定队列消息并进行处理。
有没有发现,除了队列消息处理环节是因产品不同之外,其他所有的处理机制都是一样,包括线程池构造、jedis对象池构造、jedis接口调用等。所以我设计一个抽象类,封装整个流程,把队列消息处理设计为抽象方法,让子类去实现。
代码片段
抽象的轮询器
public abstract class BasePoller {
private static ExecutorService POOL
private String queueName;
//pop指定队列消息并处理
public void popAndHandleMsgFrom(String queueName) {
//首次初始化线程池并遍历节点集群,添加执行任务
pool.execure(new PollerHandleThread(redisip,this))
}
public abstract void handle(String polledMsg);
}
队列消息处理的线程任务
public class PollerHandleThread implements Runnable{
private BasePoller poller;
private String redisip;
//pop指定队列消息并处理
public void run() {
//首次初始化jedis对象池
Jedis jedis = getJedis(redisip);
String polledMsg = jedis.pop(poller.queueName);
poller.handle(polledMsg );
}
}
产品服务子类(实现BasePoller )
public class MyPoller extends BasePoller {
public void handle(String polledMsg){
//解析队列消息,按照自己的产品业务处理
handleByMyWay(parse(polledMsg));
}
}
启动轮询
public class Launcher {
public static void main(String[] args){
MyPoller poller = new MyPoller();
poller.popAndHandleMsgFrom("Queue1");
}
}
总结
哦哦,原来上述是设计模式之一:模版方法模式,这是后来才知道的。平时比较少了解设计模式,开发过程中,也没有说认为这个模式好,这次要套上这个设计模式。
模版方法:定义一个操作中的算法的骨架,而将步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。
BasePoller 实现模板方法,定义了算法的骨架
MyPoller 实现父类抽象方法,补充了算法某个特定步骤优点:
- 模板方法模式通过把不变的操作抽取到父类,避免子类的重复代码。
- 子类实现算法的某些特定步骤,有助于算法的扩展。
缺点:
- java是单继承机制,子类只能继承一个父类。
模版方法+回调函数
背景介绍
基于redis集群维护统一,数据迁移方便,高可用的目的,部门需实现一套公用的redis客户端。虽然jedis包含了一致性算法、高可用的封装,但并不完全符合我们的要求,故基于我们的算法重新包装了jedis,其中最上层的redis操作接口出现大量重复。
代码大致如下:
- 从jedis对象池获取jedis
- 通过jedis对redis进行操作(各种操作,set,setex,get,hset,hget,llen,push,pop等等)
- 无论操作结果如何,归还对象池
哈哈,和上述有点像,那就照做吧。==,这个redis命令,少说也有几十个,按照上述方式,不是等几十个实现类。
代码片段
setex操作
public static String setex(String key, String value, int seconds) throws JedisClientException {
String errMsg = null;
UnifiedJedis jedis = JedisClient.getJedis();
try {
return jedis.setex(key, seconds, value);
} catch (JedisConnectionException e) {
if (null != jedis) {
JedisClient.returnBrokenJedis(jedis);
jedis = null;
}
} catch (Exception e) {
e.printStackTrace(System.err);
errMsg = "setex:" + e.getMessage();
} finally {
if (null != jedis) {
JedisClient.returnJedis(jedis);
}
}
throw new JedisClientException(ExceptionConstants.ERROR_OPER, errMsg);
}
hset操作
public static long hset(String key, String field, String value) throws JedisClientException {
String errMsg = null;
UnifiedJedis jedis = JedisClient.getJedis();
try {
return jedis.hset(key, field, value);
} catch (JedisConnectionException e) {
if (null != jedis) {
JedisClient.returnBrokenJedis(jedis);
jedis = null;
}
} catch (Exception e) {
e.printStackTrace(System.err);
errMsg = "hset:" + e.getMessage();
} finally {
if (null != jedis) {
JedisClient.returnJedis(jedis);
}
}
throw new JedisClientException(ExceptionConstants.ERROR_OPER, errMsg);
}
‘模版方法+回调函数’版本
setex操作
public static String setex(final String key, final String value, final int seconds) throws JedisClientException {
return template.process(new JedisClientProcessor<String>() {
@Override
public String process(UnifiedJedis jedis) {
return jedis.setex(key, seconds, value);
}
}, "method<setex>");
}
hset操作
public static String set(final String key, final String value) throws JedisClientException {
return template.process(new JedisClientProcessor<String>() {
@Override
public String process(UnifiedJedis jedis) {
return jedis.set(key, value);
}
}, "method<set>");
}
template为类JedisClientTemplate对象
public class JedisClientTemplate {
public <T> T process(JedisClientProcessor<T> processor, String extInfo) throws JedisClientException {
String errMsg = null;
UnifiedJedis jedis = JedisClient.getJedis();
try {
return processor.process(jedis);
} catch (JedisConnectionException e) {
if (null != jedis) {
JedisClient.returnBrokenJedis(jedis);
jedis = null;
}
} catch (Exception e) {
e.printStackTrace(System.err);
errMsg = extInfo + ":" + e.getMessage();
} finally {
if (null != jedis) {
JedisClient.returnJedis(jedis);
}
}
throw new JedisClientException(ExceptionConstants.ERROR_OPER, errMsg);
}
}
JedisClientProcessor为实际不同的操作实现匿名类
public interface JedisClientProcessor<T> {
T process(UnifiedJedis jedis);
}
总结
这命名方式,这代码结构,有没有似曾相识的感觉。
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)", new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, id);
ps.setString(2, name);
ps.setString(3, sex);
ps.setInt(4, age);
}
}
感觉世界又美好了一点!!
JedisClientTemplate 已有的稳定的、公用的jedis对象池
JedisClientProcessor 可变化的部分