博客原文地址:折騰Java設計模式之模板方法模式html
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.java
翻譯過來就是,把算法的框架定義好,能夠將某些步抽象出來放到子類去實現。模板方法容許子類在不改變算法框架的狀況下從新實現算法的某些步驟。git
抽象類AbstractClass
定義了算法框架templateMethod()
方法,其中有2個方法primitve1()
和primitve2()
被抽象出來,子類SubClass1
繼承了抽象類AbstractClass
,從而實現了primitve1()
和primitve2()
。github
抽象類(AbstractClass): 定義了算法核心框架,同時把局部的算法行爲封裝成步驟,讓子類去實現。算法
子類(SubClass): 繼承了抽象類,實現抽象類中的抽象方法,具體實現了算法部分邏輯。設計模式
源碼地址:Template-method緩存
先定義抽象類,抽象類AbstractProcessor
中核心算法handle
方法中大致分3部,第一先校驗參數具體怎麼校驗放在子類中實現,第二獲取結果也放在子類實現,第三獲取結果後的操做也放在子類實現。微信
@Slf4j public abstract class AbstractProcessor<P extends Request, R extends Response> { /** * 邏輯處理 * * @param request * @return */ public R handle(P request) { // 1.校驗參數 log.info("開始處理, 請求參數={}", request); validRequest(request); // 2.獲取結果 R response = getResponse(request); log.info("獲取結果, 響應結果={}", response); // 3.結果以後的處理 afterHandle(response); return response; } /** * 結果以後的處理 能夠更新其餘業務或者處理緩存 * * @param response */ protected abstract void afterHandle(R response); /** * 校驗請求參數 * * @param request */ protected void validRequest(P request) { if (Objects.isNull(request.getToken())) { throw new RuntimeException("token不能爲空"); } if (Objects.isNull(request.getVersion())) { throw new RuntimeException("version不能爲空"); } validRequestParam(request); } /** * 校驗請求真正的參數 * @param request */ protected abstract void validRequestParam(P request); /** * 獲取到結果 * @param request * @return */ protected abstract R getResponse(P request); }
基本請求框架
@Data @SuperBuilder @NoArgsConstructor @AllArgsConstructor public class Request { private String version; private String token; }
基本響應ide
@Data @SuperBuilder @NoArgsConstructor @AllArgsConstructor public class Response { private String msg; private int code; private boolean success; }
第一個子類實現
@Slf4j public class OneProcessor extends AbstractProcessor<OneRequest, OneResponse> { public OneProcessor() { ProcessorFactory.putProcess("two", this); } @Override protected void afterHandle(OneResponse response) { log.info("處理One結果: {}", response.getOne()); } @Override protected void validRequestParam(OneRequest request) { log.info("校驗one參數...省略......"); } @Override protected OneResponse getResponse(OneRequest request) { String name = request.getName(); return OneResponse.builder() .one(name + "one") .success(true) .code(0) .msg("成功") .build(); } }
第一個子類的請求
@Data @ToString(callSuper = true) @SuperBuilder @NoArgsConstructor @AllArgsConstructor public class OneRequest extends Request { private String name; @Builder.Default private int a = 0; }
第一個子類的響應
@Data @ToString(callSuper = true) @SuperBuilder @NoArgsConstructor @AllArgsConstructor public class OneResponse extends Response { private String one; }
第二個子類實現
@Slf4j public class TwoProcessor extends AbstractProcessor<TwoRequest, TwoResponse> { public TwoProcessor() { ProcessorFactory.putProcess("two", this); } @Override protected void afterHandle(TwoResponse response) { log.info("處理結果TWO, {}", response); } @Override protected void validRequestParam(TwoRequest request) { log.info("校驗TWO參數...省略......"); } @Override protected TwoResponse getResponse(TwoRequest request) { Long id = request.getId(); return TwoResponse.builder() .two("id爲"+id) .success(true) .code(0) .msg("成功") .build(); } }
第二個子類的請求
@Data @ToString(callSuper = true) @SuperBuilder @NoArgsConstructor @AllArgsConstructor public class TwoRequest extends Request { private Long id; }
第二個子類的響應
@Data @ToString(callSuper = true) @SuperBuilder @NoArgsConstructor @AllArgsConstructor public class TwoResponse extends Response { private String two; }
有的時候咱們定義的子類在Spring容器的時候由Spring定義好後,咱們其實能夠借用工廠模式方法,在子類初始化的時候就把子類放置在ProcessorFactory
中,後續只須要根據key從中拿取便可,實際項目中用這種方式仍是比較多的。
public class ProcessorFactory { private static Map<String, AbstractProcessor> factories = new HashMap(); public static void putProcess(String key, AbstractProcessor process) { factories.put(key, process); } public static AbstractProcessor selectProcess(String key) { return factories.get(key); } }
@Slf4j public class TemplateMethodApplication { public static void main(String[] args) { OneRequest oneRequest = OneRequest.builder() .version("2312312") .token("23423") .name("張三") .build(); new OneProcessor().handle(oneRequest); log.info("--------------------------"); TwoRequest twoRequest = TwoRequest.builder() .version("2312312") .token("23423") .id(23456L) .build(); new TwoProcessor().handle(twoRequest); } }
整體上來說,抽象類中定義了算法的框架,而後把部分算法步驟抽象出來供子類實現,有的時候有些方法只有個別子類會去實現,咱們能夠在抽象類中實現爲空實現,在有須要的子類中咱們能夠覆蓋抽象類的實現,這樣避免了全部的子類都去實現,其實不關心的話都是空實現了。本示例用到了lombok
的@SuperBuilder
特性,可能在下載完完整的代碼後會出現編譯錯誤,這是由於Lombok
的插件暫時還不支持@SuperBuilder
。
模板方法常常和其餘模式混合使用,好比工廠、策略等等。其實在Spring中大量使用了模板方法模式,其實也不光在Spring中,像平時jdbcTemplate或者RedisTemplate,像這種帶有Template的。
優勢 一、封裝不變部分,擴展可變部分。 二、提取公共代碼,便於維護。 三、行爲由父類控制,子類實現。
缺點 每個不一樣的實現都須要一個子類來實現,致使類的個數增長,使得系統更加龐大。
使用場景 一、有多個子類共有的方法,且邏輯相同。 二、重要的、複雜的方法,能夠考慮做爲模板方法。
注意事項 爲防止惡意操做,通常模板方法都加上 final 關鍵詞。