代码中经常遇到这样的场景:对一个业务处理过程抽象出来一个接口,针对不同的业务有不同的接口实现,然后有一个管理这些实现类的类根据业务信息负责路由到不同的实现类。实现方式大同小异,大致方式都是如此。
class XXHandlerManager {
private Map<String, IHandler> handlerKeyToHandler;
public void setHandlerKeyToHandler(Map<String,IHandler> handlers) {
this. handlerKeyToHandler = handlers;
}
public IHandler getByBizInfo(xxBizInfo ...args) {
String key = generateKey(args);
if (handlerKeyToHandler.containsKey(key)) {
return handlerKeyToHandler.get(key);
}
throw HandlerNotFoundException();
}
private String generateKey(xxBizInfo ...args) {
.....
}
}
看到我们一个业务系统很多这种基本完全一样的代码,觉得或许可以提供一个简单的抽象,从而使代码简单清晰,减少冗余代码。小小思考了下,觉得可以简化抽象的,代码如下:
public class VelocityBaseKeyMapSelector<T> {
private final static Logger LOG = LoggerFactory.getLogger(VelocityBaseKeyMapSelector.class);
/**
* 对象到字符串的转化模板
*/
private String keyTemplate;
/**
* 对象字符串到处理器的映射
*/
private Map<String, T> keyToHandler = Collections.emptyMap();
public void setKeyTemplate(String keyTemplate) {
this.keyTemplate = keyTemplate;
}
public void setKeyToHandler(Map<String, T> keyToHandler) {
this.keyToHandler = keyToHandler;
}
public T resolveHandler(Object ...objs) {
String key = translateToString(objs);
if (keyToHandler.containsKey(key)) {
T handler = keyToHandler.get(key);
if (LOG.isDebugEnabled()) {
LOG.debug("found handler instance ({}) for data ({}).", handler.getClass().getSimpleName(), objs);
}
return handler;
}
return null;
}
/**
* 根据对象信息以及Velocity模板生成标识Key
*/
protected String translateToString(Object ...objs) {
Map<String, Object> ctx = Maps.newHashMap();
ctx.put("args", Lists.newArrayList(objs));
return VelocityUtil.rend(ctx, keyTemplate);
}
}
通过三个点实现了通用,1.使用泛型使得代码更加通用,对于key觉得没必要提取泛型;2.生成key的业务参数使用变参,表示不限制业务参数的维度;3.把“由业务参数生成key的算法”封装在了keyTemplate模板字符串,样式类似$args.get(0).name,预发满足Velocity语法即可,其中args是所有决定key的参数的List,顺序和方法中顺序一致,综合这三点这个工具类基本完全通用了,最起码我们的业务中的类似代码被消除了,事实上,业务完全不需要扩展,因为已经可变的部分交给配置了。结合Spring就会更舒服,来个小例子:
<bean id="platformBasedSelector" class="xxxxxx.VelocityBasedKeyMapSelector"> <property name="keyToHandler"> <map> <entry key="xxx" value-ref="xxxHandler" /> <entry key="yyy" value-ref="yyyHandler" /> <entry key="zzz" value-ref="zzzHandler" /> </map> </property> <property name="keyTemplate" value="$!args.get(0).platformId" /> </bean>
这样每个需要此类功能代码只需要配置一下注入即可,对于key算法的改写也就没必要去修改Java代码了,直接修改keyTemplate对应的模板字符串即可,当然也需要修改上边的handler对应的key。enjoy it!