Spring Boot 策略模式

banq發表於2021-06-09

相信大部分開發者都見過這樣的程式碼:

 @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 應用程式中實現它。

相關文章