設計模式-模板方法

来源:https://www.cnblogs.com/uniqueDong/archive/2019/06/21/11066399.html
-Advertisement-
Play Games

模板方法 關註公眾號 JavaStorm 獲取更多精彩。 模板方法模式在一個方法中定義了一個演算法骨架,並且 final 修飾防止子類重寫。方法中包含一些抽象方法,也就是一些步驟延遲到字類實現。模板方法使得在不改變演算法結構的情況下,重新定義演算法中的某些步驟。完整代碼可以查看GitHub: "https ...


模板方法

關註公眾號 JavaStorm 獲取更多精彩。

模板方法模式在一個方法中定義了一個演算法骨架,並且 final 修飾防止子類重寫。方法中包含一些抽象方法,也就是一些步驟延遲到字類實現。模板方法使得在不改變演算法結構的情況下,重新定義演算法中的某些步驟。完整代碼可以查看GitHub:https://github.com/UniqueDong/zero-design-stu

類圖

模板方法模式

模式實現

在實現模板方法模式時,開發抽象類的軟體設計師和開發具體子類的軟體設計師之間可以進行協作。一個設計師負責給出一個演算法的輪廓和框架,另一些設
計師則負責給出這個演算法的各個邏輯步驟。實現這些具體邏輯步驟的方法即為基本方法,而將這些基本方法彙總起來的方法即為模板方法,模板方法模式的
名字也因此而來。下麵將詳細介紹模板方法和基本方法:

  1. 模板方法
    一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總演算法或一個總行為的方法。這個模板方法定義在抽象類中,並由子類不加以修改
    地完全繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中可以是具體方法,也可以是抽象方法。由於模板方法
    是具體方法,因此模板方法模式中的抽象層只能是抽象類,而不是介面。
  2. 基本方法
    基本方法是實現演算法各個步驟的方法,是模板方法的組成部分。基本方法又可以分為三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。 (1) 抽象方法:一個抽象方法由抽象類聲明、由其具體子類實現。 (2) 具體方法:一個具體方法由一個抽象類或具體類聲明並實現,其子類可以進行覆蓋也可以直接繼承。 (3) 鉤子方法:一個鉤子方法由一個抽象類或具體類聲明並實現,而其子類可能會加以擴展。通常在父類中給出的實現是一個空實現,並以該空實
    現作為方法的預設實現,當然鉤子方法也可以提供一個非空的預設實現。
    鉤子可以讓子類實現演算法中可選的部分,或者在鉤子對於子類的實現並不重要的時候,子類可以對此鉤子置之不理。鉤子的另一個用法,是讓子類能夠有機會
    對模板方法中某些即將發生的(或剛剛發生的)步驟做出反應。

使用場景

開一家咖啡、茶館,泡茶和咖啡的沖泡方式非常相似:

星巴克咖啡沖泡法

  1. 把水煮沸
  2. 用沸水沖泡咖啡
  3. 把咖啡倒進杯子
  4. 加糖和牛奶

功夫茶沖泡法

  1. 把水煮沸
  2. 用沸水沖泡茶葉
  3. 把茶倒進杯子
  4. 加檸檬

我們可以發現兩種茶的步驟1,和步驟3是一樣的。整體演算法結構是固定的,只是有的部分不一樣。這時候我們就可以使用模板方法設計模式定義製作骨架,然後部分細節留給子類實現。

代碼實現

首先我們先抽象一個製作飲料的模板,定義演算法邏輯 AbstractBeverage。同時有一個鉤子方法,一般是空實現,在這裡我們可以通過它(customerWantsCondiments())來控制是否加調料。

package com.zero.design.actions.template;

/**
 * 抽象製作飲料模板:定義演算法骨架
 */
public abstract class AbstractBeverage {
    /**
     * 這就是模板方法。它被聲明為final,以免子類改變這個演算法的順序。
     * 演算法步驟組合
     */
    final void prepareRecipe() {
        // 模板方法定義了一連串的步驟,每個步驟由一個方法代表
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    /**
     *  我們在這裡定義了一個方法,(通常)是空的預設實現。這個方法只會返回true,不做別的事。
     * 這就是一個鉤子,子類可以覆蓋這個方法,但不見得一定要這麼。
     * @return
     */
    boolean customerWantsCondiments() {
        return true;
    }

    /**
     * 添加佐料:不同飲料也有不同佐料:申明為抽象類,由子類取操心
     */
    protected abstract void addCondiments();
    /**
     * 釀製:不同飲料方式也不同,申明為抽象類,由子類取操心
     */
    protected abstract void brew();

    /**
     * 共通方法:倒入杯中
     */
    public void pourInCup() {
        System.out.println("倒入杯子中...");
    }

    /**
     * 把水煮沸,共通方法
     */
    public void boilWater() {
        System.out.println("把水煮沸...");
    }
}

接著我們定義泡茶具體演算法,並且繼承 AbstractBeverage 抽象演算法,實現泡茶的具體邏輯。

package com.zero.design.actions.template;

public class Tea extends AbstractBeverage {

    /**
     * 這樣通過鉤子就可以選擇是都要加佐料了
     */
    private boolean addCondiments = true;

    /**
     * 添加糖、牛奶
     */
    @Override
    protected void addCondiments() {
        System.out.println("添加檸檬,茶更好喝");
    }

    /**
     * 咖啡沖泡方法
     */
    @Override
    protected void brew() {
        System.out.println("秘制泡茶方式放入茶葉");
    }

    /**
     * 使用鉤子,不加佐料
     * @return
     */
    @Override
    boolean customerWantsCondiments() {
        return addCondiments;
    }

    public boolean isAddCondiments() {
        return addCondiments;
    }

    public void setAddCondiments(boolean addCondiments) {
        this.addCondiments = addCondiments;
    }
}

定義咖啡的演算法細節

package com.zero.design.actions.template;

/**
 * 咖啡具體實現:只需要自行處理沖泡和添加調料部分
 */
public class Coffe extends AbstractBeverage {

    /**
     * 這樣通過鉤子就可以選擇是都要加佐料了
     */
    public boolean addCondiments = true;


    /**
     * 添加糖、牛奶
     */
    @Override
    protected void addCondiments() {
        System.out.println("添加糖跟牛奶");
    }

    /**
     * 咖啡沖泡方法
     */
    @Override
    protected void brew() {
        System.out.println("放入咖啡豆,使用秘制方法沖泡");
    }

    /**
     * 重寫鉤子
     * @return
     */
    @Override
    boolean customerWantsCondiments() {
        return addCondiments;
    }

    public boolean isAddCondiments() {
        return addCondiments;
    }

    public void setAddCondiments(boolean addCondiments) {
        this.addCondiments = addCondiments;
    }
}

接著就是客戶點單,我們通過模板方法模式製作咖啡或者功夫茶。達到代碼復用。

package com.zero.design.actions.template;

/**
 * Created by unique on 2017/6/7.
 */
public class Test {

    public static void main(String[] args) {
        Tea tea = new Tea();
        tea.setAddCondiments(false);
        tea.prepareRecipe();
        System.out.println("-------------------");
        Coffe coffe = new Coffe();
        coffe.prepareRecipe();
    }

}

輸出如下

把水煮沸...
秘制泡茶方式放入茶葉
倒入杯子中...
-------------------
把水煮沸...
放入咖啡豆,使用秘制方法沖泡
倒入杯子中...
添加糖跟牛奶

模板方法模式的優缺點

優點

1)良好的封裝性。把公有的不變的方法封裝在父類,而子類負責實現具體邏輯。

2)良好的擴展性:增加功能由子類實現基本方法擴展,符合單一職責原則和開閉原則。

3)復用代碼。

缺點

1)由於是通過繼承實現代碼復用來改變演算法,靈活度會降低。

2)子類的執行影響父類的結果,增加代碼閱讀難度。

點贊與收藏是最大的鼓勵。歡迎關註公眾號 JavaStorm
JavaStorm


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 註:本文基於上一篇文章【 Vue-Cli 3.0 中配置高德地圖 】 ,採用直接引入高德 SDK 的方式來使用高德地圖api 一、效果圖 二、組件要實現的功能 1. 如果有傳入坐標點,則定位到坐標點 2. 如果沒有傳入坐標點,則定位到當前所在位置 3. 定位成功要在右側顯示經緯度和地址 4. 可以通 ...
  • HTML:HTML4.01 指的就是網頁技術HTML5:HTML4.01的升級版本 1.web的基礎知識 web與Internet1.Internet:全球性的電腦互聯網路,網際網路,互聯網,交互網2.提供服務 3.Internet上的應用程式 4.web 2.HTML快速入門 1.什麼是HTML? ...
  • 效果圖: 問題:行hover效果感覺錯亂 所以改為透明色 代碼: ...
  • 官方文檔 : https://github.com/yiminghe/async-validator ...
  • 用法 先來看看官網的介紹: 主要有八個生命周期,分別是: beforeCreate、created、beforeMount、mounted、beforeupdate、updated 、beforeDestroy和destroyed,分別對應八個不同的時期,另外還有兩個activated和deacti ...
  • 創造一個自適應的盒子,跟隨瀏覽器寬高,目的是不覆蓋body元素 css部分 html部分 js 部分生成自適應盒子 圖片元素填入盒子,點擊關閉隱藏並還原圖片 若是多張圖片可用for迴圈遍歷元素自行改造 ...
  • 作者 | huashiouhttps://segmentfault.com/a/1190000018626163 1. 概述 本文以淘寶作為例子,介紹從一百個併發到千萬級併發情況下服務端的架構的演進過程,同時列舉出每個演進階段會遇到的相關技術,讓大家對架構的演進有一個整體的認知,文章最後彙總了一些架 ...
  • 一. 本單元兩次作業的架構設計 終於在最後一個單元實現了增量式開發。在本單元第一次作業中,我便構建了圖數據結構,第二次作業變得比較簡單,無需重構,只是增加狀態圖和順序圖即可,下麵分析第二次作業的架構設計。 1. 頂級類 MyUmlGeneralInteraction 負責將輸入歸類,形成3個子Uml ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...