栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring几种使用策略设计模式的方式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring几种使用策略设计模式的方式

同学们可以看看,项目中是不是有这样类似的代码

问题不是很大,但是可能经常要改,只能一个小哥维护

    public static void sendHttpRequest(HttpMethod httpMethod, Map paramsMap) {
        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 clazz, Map paramsMap) {
  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, Map paramsMap) {
}

在枚举文件稍加文档说明就可以把事情说清楚

所以我们需要将枚举和策略实现类进行一个绑定,可以使用这种简单粗暴的方式,问题也不是很大,多一个策略就多一个枚举值,没毛病

public enum HttpMethod {

  GET(GetMethodHandler.class),

  POST(PostMethodHandler.class);

  public Class getClazz() {
    return clazz;
  }

  private final Class clazz;

  HttpMethod(Class clazz) {
    this.clazz = clazz;
  }
}

代码如下,使用SpringUtil通过class获取Bean

 public static void sendHttpRequest(HttpMethod httpMethod, Map paramsMap) {
   	  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, Map paramsMap) {
  	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,简直无敌啊.
  • 在我们的接口上多写一些注解和参数说明,其他人也可以一起使用,一起写一个实现类,但是要注意事项说清楚.
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/282294.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号