Spring Boot 策略模式


相信大部分开发者都见过这样的代码:

 @Component
  class RequestReceiver {

    public Response receiveRequest(Request request) {
        switch (request.getType()) {
            case GET:
                return handleGet(request);
            case POST:
                return handlePost(request);
            case PUT:
                return handlePut(request);
            default:
                throw new IllegalArgumentException("Unknown request type: " + request.getType());
        }
    }
}

乍一看,这个实现没有任何问题,看起来很干净。
但是,假设我们要添加对 DELETE、PATCH 和 HEAD HTTP 方法的支持。为此,我们需要为此类中的每个方法实现处理逻辑,并将它们包含在 switch 块中。这完全违反了开闭原则,该原则指出:软件实体应该对扩展开放,但对修改关闭。根据我自己的经验,我看到过一些长达数百行的 switch 语句,我向您保证,这样的代码很混乱且难以测试。如果我们可以在不触及现有逻辑的情况下添加对新 HTTP 方法的支持,那就太好了。这就是策略设计模式可以帮助我们的地方。
 
策略模式是一种行为软件设计模式,可以在运行时选择算法。代码不是直接实现单个算法,而是接收关于在一系列算法中使用哪个的运行时指令。
让我们尝试应用此模式并重新实现我们的RequestReceiver 类。首先,我们需要定义一个新的抽象,负责处理单个请求。
interface RequestHandler {
    Response handle(Request request);

    RequestType supportedRequestType();
}

@Component
class GetRequestHandler implements RequestHandler {

    @Override
    public Response handle(Request request) {...}

    @Override
    public RequestType supportedRequestType() {
        return RequestType.GET;
    }
}

@Component
class PostRequestHandler implements RequestHandler {

    @Override
    public Response handle(Request request) {...}

    @Override
    public RequestType supportedRequestType() {
        return RequestType.POST;
    }
}

@Component
class PutRequestHandler implements RequestHandler {

    @Override
    public Response handle(Request request) {...}

    @Override
    public RequestType supportedRequestType() {
        return RequestType.PUT;
    }
}

创建新的抽象后,有几种方法可以使用它们,我将展示其中的几种:
  • 使用Map存储策略
  • 使用专用注册表来存储策略

 
1. 使用 Map 实现

@RequiredArgsConstructor
class RequestReceiver {

  private final Map<RequestType, RequestHandler> requestHandlers;

  public Response receiveRequest(Request request) {
      if (!requestHandlers.containsKey(request.getType())) {
          throw new IllegalArgumentException("Unknown request type: " + request.getType());
      }
     return requestHandlers.get(request.getType()).handle(request);
  }
}

我们需要用策略填充我们的Map,为此,我们创建了一个配置类。

@Configuration
class RequestReceiverConfiguration {

    @Bean
    public RequestReceiver requestReceiver(List<RequestHandler> requestHandlers) {
        return new RequestReceiver(
                requestHandlers.stream()
                        .collect(toMap(RequestHandler::supportedRequestType, Function.identity()))
        );
    }
}

 
2. 使用专用注册表实现
让我们为RequestHandlers实现一个注册表:
@Component
@RequiredArgsConstructor
class RequestHandlerRegistry {

    private final List<RequestHandler> requestHandlers;

    public Optional<RequestHandler> getHandlerFor(Request request) {
        return requestHandlers.stream()
                .filter(handler -> handler.supportedRequestType().equals(request.getType()))
                .findAny();
    }
}

RequestReceiver 的最终实现:

@Component
@RequiredArgsConstructor
class RequestReceiver {

    private final RequestHandlerRegistry registry;

    public Response receiveRequest(Request request) {
        return registry.getHandlerFor(request)
                .orElseThrow(() -> new IllegalArgumentException("Unknown request type: " + request.getType()))
                .handle(request);
    }
}

重构后,这两种方法都使我们的设计隔离,同时对扩展开放,现在我们可以轻松地为 DELETE 方法添加新的实现,而无需更改一行现有代码:


@Component    
class DeleteRequestHandler implements RequestHandler {

    @Override
    public Response handle(Request request) {}

    @Override
    public RequestType supportedRequestType() {
        return RequestType.DELETE;
    }
}


结论
在本文中,我们解释了策略模式,并演示了如何在 Spring Boot 应用程序中实现它。