同学们可以看看,项目中是不是有这样类似的代码
问题不是很大,但是可能经常要改,只能一个小哥维护
public static void sendHttpRequest(HttpMethod httpMethod, MapparamsMap) { switch (httpMethod) { case GET: //doGet(paramsMap); case PUT: //doPut(paramsMap); case HEAD: //doHead(paramsMap); case POST: //doPost(paramsMap); case PATCH: //doPatch(paramsMap); case TRACE: //doTrace(paramsMap); case DELETE: //doDelete(paramsMap); case OPTIONS: //doOptions(paramsMap); } }
废话不多说直接上代码
先定义一个接口
public interface HttpMethodHandler {
void sendRequest(Map map);
}
为get请求写一个实现类
@Component
public class GetMethodHandler implements HttpMethodHandler{
@Override
public void sendRequest(Map map) {
doGet(map);
}
}
然后修改一下调用的方法,只需要传入一个处理器的clazz即可
public static void sendHttpRequest(Class extends HttpMethodHandler> clazz, MapparamsMap) { MethodHandler handler = SpringUtil.getBean(clazz); if(handler == null){ throw new RuntimeException("no such http hanlder"); } handler.sendRequest(paramsMap);
这是一个比较简单的基于Spring的策略设计模式的实现.
有个小问题,那就是传入clazz这种方式只有开发的人比较熟悉,有什么什么实现类可以干什么什么事情,如果是我们日常工作的时候需要其他小伙伴调用的话,会有一些理解上的出入,一般情况下,我们使用枚举比较多.
public void sendHttpRequest(HttpMethod httpMethod, MapparamsMap) { }
在枚举文件稍加文档说明就可以把事情说清楚
所以我们需要将枚举和策略实现类进行一个绑定,可以使用这种简单粗暴的方式,问题也不是很大,多一个策略就多一个枚举值,没毛病
public enum HttpMethod {
GET(GetMethodHandler.class),
POST(PostMethodHandler.class);
public Class extends HttpMethodHandler> getClazz() {
return clazz;
}
private final Class extends HttpMethodHandler> clazz;
HttpMethod(Class extends HttpMethodHandler> clazz) {
this.clazz = clazz;
}
}
代码如下,使用SpringUtil通过class获取Bean
public static void sendHttpRequest(HttpMethod httpMethod, MapparamsMap) { HttpMethodHandler handler = SpringUtil.getBean(httpMethod.getClazz()); handler.sendRequest(paramsMap); }
实际上我们项目中,使用的另一种方法: 注解 + 枚举
@Target(ElementType.TYPE)
@documented
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpMethodStrategy {
HttpMethod value();
}
实现类添加我们定义的注解,并指定类型
@Component
@HttpMethodStrategy(HttpMethod.GET)
public class GetMethodHandler implements HttpMethodHandler {
@Override
public void sendRequest(Map map) {
//doGet(map);
}
}
写一个容器Map,将策略都塞进Map里,key是枚举,value是Bean.
简单说明一下,首先我们通过getBeanByAnnotation()方法获取方法上含有指定注解的Bean,这些Bean可能并没有实现HttpMethodHandler接口,这里需要我们开发自己保证.
然后我们将HttpMethod做为key,Handler处理器方法作为value,这样我们就做好一个枚举转换成策略实现类的一个工具类
@Component public class HttpMethodContext implements ApplicationListener{ private static final Map handlerMap = new EnumMap (HttpMethod.class); public static HttpMethodHandler getHandler(HttpMethod httpMethod) { return handlerMap.get(httpMethod); } @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { Map beans = contextRefreshedEvent.getApplicationContext().getBeansWithAnnotation(HttpMethodStrategy.class); beans.forEach((key, bean) -> { HttpMethodStrategy strategy = bean.getClass().getAnnotation(HttpMethodStrategy.class); HttpMethod httpMethod = strategy.value(); handlerMap.put(httpMethod, (HttpMethodHandler) bean); }); } }
我们完善一下工具类的代码
public void sendHttpRequest(HttpMethod httpMethod, MapparamsMap) { HttpMethodHandler handler = HttpMethodContext.getHanlder(httpMethod); handler.sendRequest(paramsMap); }
简单的说,我们将一个枚举来映射一个Bean,来实现策略设计模式的.
我再举几个项目中使用到的例子.
MQ消费比如我们系统收到一个MQ消息,我们已经得到了topi,tag和message内容,我们现在要进行消费,随着业务的发展,这些消息的数量越来越多,需要我们不断地去拓展消息类型.
先定义一个消息消费者
public interface RocketMQMsgConsumer {
default boolean match(String topic,String tag){
return false;
}
default boolean consume(String message){
return false;
}
}
写一个消费者存储列表
@Component public class RocketMQMsgConsumerContext implements ApplicationListener{ private static final List ConSUMERS = new ArrayList<>(); @Override public void onApplicationEvent(ContextRefreshedEvent event) { Map beanMap = event.getApplicationContext().getBeansOfType(RocketMqMsgConsumer.class); CONSUMERS.addAll(beanMap.values()); } public List getConsumers(String topic,String tag){ return CONSUMERS.stream() .filter(item -> item.match(topic,tag)) .collect(Collectors.toList()); } }
mq消费监听器
这里我们的消费者可能是多个,只有全部成功才能成功,否则会提示RocketMQ等下再发消息过来.大家可以也可以设置一个consumer
@Component
public class MessageConsumerListener implements MessageListener {
@Override
public Action consume(Message message, ConsumeContext context) {
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
List consumers = RocketMQMsgConsumerContext.getConsumers(message.getTopic(), message.getTag());
boolean result = true;
for (RocketMqMsgConsumer consumer : consumers) {
result &= consumer.consume(msg);
}
return result ? Action.CommitMessage : Action.ReconsumeLater;
}
}
其他例子,也有很多,讲一下可拓展的点
- 像前端在页面上配置了某个配置项(这个配置项下拉框列表可以直接给Enum.values()),然后存储到后台数据库,假设存储的值为 1,2,3,4,5 ,然后我们取出这个配置值,再用我们的context获取一个策略类,然后执行其中的方法.这样我们后端只需要定义策略类的类型,并进行一个实现,需要加一个策略就多一个枚举值,再写一个策略实现方法.
- 策略方法之间还可以使用一些继承,比如都继承一个abstractHandler,之类的,拓展性拉满
- getHandler()这个方法如果找不到对应的策略类还可以整一个默认的DefaultHandler,简直无敌啊.
- 在我们的接口上多写一些注解和参数说明,其他人也可以一起使用,一起写一个实现类,但是要注意事项说清楚.



