分析設(shè)計(jì)模式之模板方法Java實(shí)現(xiàn)
一、什么是模板方法設(shè)計(jì)模式
從字面意義上理解, 模板方法就是定義出來(lái)一套方法, 作為模板, 也就是基礎(chǔ)。 在這個(gè)基礎(chǔ)上, 我們可以進(jìn)行加工,實(shí)現(xiàn)個(gè)性化的實(shí)現(xiàn)。比如:一日餐三. 早餐, 中餐, 晚餐. 每個(gè)人都要吃三餐, 但每個(gè)人的三餐吃的可能都不一樣. 一日三餐定義了模板--早中晚, 每個(gè)人的三餐就是模板的具體實(shí)現(xiàn).
1.1、模板方法的用途
- 將不變的行為從子類搬到超類,去除了子類中的重復(fù)代碼
- 規(guī)范子類的結(jié)構(gòu)
1.2、模板方法的定義
定義一個(gè)操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變?cè)撍惴ńY(jié)構(gòu)的情況下重定義該算法的某些特定步驟。它是一種類行為型模式。
二、定義模板方法的步驟
2.1、定義模板類
通常模板類是抽象類,負(fù)責(zé)給出算法的輪廓或者框架。他是有若干個(gè)模板方法和若干個(gè)基本方法構(gòu)成。
1.模板方法
定義了算法的骨架, 定義了方法調(diào)用的順序, 其中包含一個(gè)或者多個(gè)基本方法
2.基本方法
基本算法有三種類型:
a) 抽象方法:子類必須重寫的方法。沒(méi)有默認(rèn)實(shí)現(xiàn)。
b)具體方法:父類定義的默認(rèn)實(shí)現(xiàn),有實(shí)現(xiàn)邏輯,可以被具體的子類繼承或重寫
c)鉤子方法:判斷的邏輯方法和需要子類重寫的空方法兩種。
2.2、定義具體子類
具體子類,也就是具體的實(shí)現(xiàn)類, 實(shí)現(xiàn)抽象類中的抽象方法。他們是抽象的模板方法中一個(gè)組成部分。
2.3、定義客戶端調(diào)用
客戶端調(diào)用抽象類, 實(shí)例化的時(shí)候?qū)嵗唧w類, 只需要調(diào)用抽象類的模板方法就可以了。
2.4 下抽象類和子類之間的UML圖和源碼實(shí)現(xiàn)
1.UML圖
從圖中可以看出抽象類的結(jié)構(gòu)可以定義三類方法。 可以有一個(gè)也可以有多個(gè)。子類必須需要實(shí)現(xiàn)抽象類中的抽象方法,可以選擇性重寫父類的具體方法。子類實(shí)現(xiàn)接口的時(shí)候,要多思考設(shè)計(jì)模式的六大原則。
2.源碼
先定義抽象類, 也就是框架。
package com.lxl.www.designPatterns.templatePattern.template; /** * 抽象類, 定義模板 */ public abstract class AbstractClass { /** * 定義模板方法 * 規(guī)范了流程的框架 */ public void templateMethod() { // 先調(diào)用具體方法 specificMethod(); // 在調(diào)用抽象方法 abstractMethod(); } /** * 具體方法 */ public void specificMethod() { // 具體的公共邏輯, 父子類通用 System.out.println("具體方法---父子類通用邏輯"); } /** * 抽象方法 * * 抽象方法, 子類必須重寫 */ public abstract void abstractMethod(); }
在定義具體的實(shí)現(xiàn)類, 實(shí)現(xiàn)父類的抽象方法
package com.lxl.www.designPatterns.templatePattern.template; /** * 具體實(shí)現(xiàn)類 */ public class ConcreteClass extends AbstractClass{ /** * 重寫父類的抽象方法 */ @Override public void abstractMethod() { System.out.println("具體實(shí)現(xiàn)類--重寫父類的抽象方法"); } }
最后定義客戶端調(diào)用
package com.lxl.www.designPatterns.templatePattern.template; /** * 模板方法客戶端 */ public class TemplateClient { public static void main(String[] args) { AbstractClass abstractClass = new ConcreteClass(); abstractClass.templateMethod(); } }
運(yùn)行結(jié)果:
具體方法---父子類通用邏輯
具體實(shí)現(xiàn)類--重寫父類的抽象方法
對(duì)照模板方法設(shè)計(jì)模式,我們來(lái)看一個(gè)具體的案例。
三、案例
3.1、案例1: 一日規(guī)劃
每個(gè)人的一日安排都有三餐, 早餐, 中餐,晚參。 但每個(gè)人的三餐食物不盡相同,我們來(lái)看看每個(gè)人的三餐變化, 以及三餐前后要做的事情。
package com.lxl.www.designPatterns.templatePattern.oneDayArrangement; /** * 一日三餐抽象類 */ public abstract class ArrangementAbstract { /** * 模板方法 * 規(guī)定了一天的框架 */ public void templateMethod() { System.out.println("一日安排如下: "); getUp(); breakfast(); lunch(); dinner(); getDown(); } public void getUp() { System.out.println("起床"); } public void getDown() { System.out.println("睡覺(jué)"); } /** * 早餐抽象類 */ public abstract void breakfast() ; /** * 午餐抽象類 */ public abstract void lunch(); /** * 晚餐抽象類 */ public abstract void dinner(); }
定義一日三餐抽象類。每個(gè)人的日程安排都是,起床,早餐,中餐,晚餐,睡覺(jué)。 其中起床和睡覺(jué)是每個(gè)人都要做的事情,三餐也是,但三餐的食物不同,于是我們將三餐定義為抽象
一日安排實(shí)現(xiàn)類
package com.lxl.www.designPatterns.templatePattern.oneDayArrangement; /** * 張三的一日三餐安排 */ public class PersonArrangement extends ArrangementAbstract{ private String name; public PersonArrangement(String name) { this.name = name; } /** * 早餐抽象類 */ public void breakfast(){ System.out.println(name + "--早餐吃牛奶面包"); } /** * 午餐抽象類 */ public void lunch() { System.out.println(name + "--中餐吃食堂"); } /** * 晚餐抽象類 */ public void dinner() { System.out.println(name + "--晚餐吃水果"); } }
客戶端調(diào)用
public class Client { public static void main(String[] args) { ArrangementAbstract zhangsan = new PersonArrangement("張三"); zhangsan.templateMethod(); } }
運(yùn)行結(jié)果:
一日安排如下:
起床
張三--早餐吃牛奶面包
張三--中餐吃食堂
張三--晚餐吃水果
睡覺(jué)
可以看出, 完全按照模板方法的步驟實(shí)現(xiàn)。
3.2、案例2: 鉤子方法
我們上面說(shuō)了, 模板方法設(shè)計(jì)模式中, 基本方法包括抽象方法,具體方法和鉤子方法.
如果能夠使用好鉤子方法, 可以在程序中完美實(shí)現(xiàn)子類控制父類的行為. 我們來(lái)看下面的案例:
我們?cè)诔橄蠓椒ㄖ卸x一個(gè)鉤子方法hookMethod(), 在模板方法templateMethod()中,鉤子方法控制了代碼的流程.
UML圖:
源代碼:
package com.lxl.www.designPatterns.templatePattern.hookMethod; /** * 抽象類, 定義模板 */ public abstract class AbstractClass { /** * 定義模板方法 * 規(guī)范了流程的框架 */ public void templateMethod() { // 調(diào)用具體方法 specificMethod(); // 鉤子方法控制下一步驟 if (hookMethod()) { // 調(diào)用抽象方法 abstractMethod(); } } /** * 具體方法 */ public void specificMethod() { // 具體的公共邏輯, 父子類通用 System.out.println("具體方法---父子類通用邏輯"); } /** * 鉤子方法 * 鉤子方法是有具體實(shí)現(xiàn)的, */ public boolean hookMethod() { return true; } /** * 抽象方法 * * 抽象方法, 子類必須重寫 */ public abstract void abstractMethod(); }
定義具體實(shí)現(xiàn)
/** * 具體實(shí)現(xiàn)類 */ public class ConcreteClass extends AbstractClass { /** * 重寫父類的抽象方法 */ @Override public void abstractMethod() { System.out.println("具體實(shí)現(xiàn)類--重寫父類的抽象方法"); } /** * 鉤子方法 * @return */ @Override public boolean hookMethod() { System.out.println("重寫了父類的鉤子方法, 反向控制父類的行為"); return false; } }
重寫了鉤子方法, 反向控制父類的行為
public class TemplateClient { public static void main(String[] args) { AbstractClass abstractClass = new ConcreteClass(); abstractClass.templateMethod(); } }
運(yùn)行結(jié)果
具體方法---父子類通用邏輯
重寫了父類的鉤子方法, 反向控制父類的行為
如果子類鉤子方法 HookMethod() 的代碼改變,則程序的運(yùn)行結(jié)果也會(huì)發(fā)生改變。
四、模板方法的優(yōu)缺點(diǎn)
4.1、優(yōu)點(diǎn)
1.規(guī)范了框架, 封裝了不變的部分, 擴(kuò)展了可變的部分. 父類定義框架, 并抽象了公共不變的部分, 子類通過(guò)重寫擴(kuò)展完善了框架的實(shí)現(xiàn)。
2.使用了"開閉原則", 對(duì)擴(kuò)展開放, 對(duì)修改關(guān)閉. 子類可以通過(guò)重寫父類的抽象方法來(lái)擴(kuò)展父類的實(shí)現(xiàn)。
3.行為集中有父類控制, 規(guī)范流程。
4.2、缺點(diǎn)
1.每一種實(shí)現(xiàn)都需要定義一個(gè)具體實(shí)現(xiàn)類, 增加類的數(shù)量, 系統(tǒng)更加復(fù)雜。
2.繼承的缺點(diǎn), 一旦父類增加一個(gè)抽象方法, 所有子類都需要增加. 這一點(diǎn)違背"開閉原則"。
3.父類中的抽象方法由子類實(shí)現(xiàn), 子類的執(zhí)行結(jié)果影響父類, 這種"反向控制"結(jié)構(gòu), 會(huì)增加代碼的復(fù)雜性。
五、使用場(chǎng)景
1.算法的整體步驟是固定的,但個(gè)別部分容易發(fā)生變化時(shí),可以考慮使用模板方法設(shè)計(jì)模式,將容易發(fā)生變化的部分抽象出來(lái),提供給子類去實(shí)現(xiàn)。
2.當(dāng)多個(gè)子類存在公共的行為時(shí),可以將其提取出來(lái)并集中到一個(gè)公共父類中以避免代碼重復(fù)。首先,要識(shí)別現(xiàn)有代碼中的不同之處,并且將不同之處分離為新的操作。最后,用一個(gè)調(diào)用這些新的操作的模板方法來(lái)替換這些不同的代碼。
3.當(dāng)需要控制子類的擴(kuò)展時(shí),模板方法只在特定點(diǎn)調(diào)用鉤子操作,這樣就只允許在這些點(diǎn)進(jìn)行擴(kuò)展。
4.重構(gòu)時(shí),模板方法模式是一個(gè)經(jīng)常使用到的模式,把相同的代碼抽取到父類中,通過(guò)鉤子函數(shù)約束其行為
六、對(duì)設(shè)計(jì)模式六大原則的應(yīng)用思考
1.單一職責(zé)原則: 一個(gè)方法只有一個(gè)引起變化的原因, 這個(gè)不太好看出, 要開子類代碼的具體實(shí)現(xiàn)
2.里式替換原則: 父類出現(xiàn)的地方都可以使用子類替換,并且結(jié)果保持一致. 子類重寫了父類的方法。 模板方法設(shè)計(jì)模式可能違背里式替換原則, 不過(guò),這正是能夠“反向控制”的原理
3.接口隔離原則: 依賴于最小的單一接口, 而不是胖接口. 符合
4.依賴倒置原則: 依賴于抽象, 而不是依賴于具體. 符合
5.迪米特法則: 最少知識(shí)原則. 之和朋友溝通, 減少和朋友的溝通. 這個(gè)需要看子類具體實(shí)現(xiàn)是否符合
6.開閉原則: 違背開閉原則, 一旦父類增加一個(gè)抽象方法, 所有子類都需要對(duì)應(yīng)增加
以上就是分析設(shè)計(jì)模式之模板方法的詳細(xì)內(nèi)容,更多關(guān)于設(shè)計(jì)模式 模板方法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Java設(shè)計(jì)模式之模板方法模式
- Java行為型設(shè)計(jì)模式之模板方法詳解
- Java設(shè)計(jì)模式之模板方法詳解
- Java設(shè)計(jì)模式之模板方法模式詳解
- 深入理解Java設(shè)計(jì)模式之模板方法模式
- Java設(shè)計(jì)模式之java模板方法模式詳解
- Java設(shè)計(jì)模式之淺談模板方法模式
- Java設(shè)計(jì)模式模板方法(Template)原理解析
- Java設(shè)計(jì)模式模板方法模式(Template)用法解析
- Java經(jīng)典設(shè)計(jì)模式之模板方法模式定義與用法示例
- java設(shè)計(jì)模式之模板方法模式詳解
- 舉例講解Java設(shè)計(jì)模式編程中模板方法模式的運(yùn)用實(shí)例
- 深入解析Java的設(shè)計(jì)模式編程中的模板方法模式
相關(guān)文章
Java Swing編寫一個(gè)簡(jiǎn)單的計(jì)算器軟件
大家好,本篇文章主要講的是Java Swing編寫一個(gè)簡(jiǎn)單的計(jì)算器軟件,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12Struts2源碼分析之ParametersInterceptor攔截器
這篇文章主要介紹了Struts2源碼分析之ParametersInterceptor攔截器,ParametersInterceptor攔截器其主要功能是把ActionContext中的請(qǐng)求參數(shù)設(shè)置到ValueStack中,,需要的朋友可以參考下2019-06-06Nacos通過(guò)RefreshScope實(shí)現(xiàn)配置自動(dòng)更新的方式分享
這篇文章主要給大家介紹了Nacos如何通過(guò)RefreshScope實(shí)現(xiàn)配置自動(dòng)更新,文中給了兩種實(shí)現(xiàn)方式供大家參考,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-09-09C語(yǔ)言中下標(biāo)與指針的轉(zhuǎn)換以及指向指針的指針的例子
這篇文章主要介紹了C語(yǔ)言中下標(biāo)與指針的轉(zhuǎn)換以及指向指針的指針的示例,是C語(yǔ)言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11Java8 新特性之日期時(shí)間對(duì)象及一些其他特性
這篇文章主要介紹了Java8 新特性之日期時(shí)間對(duì)象及一些其他特性,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01Springboot集成graylog及配置過(guò)程解析
這篇文章主要介紹了Springboot集成graylog及配置過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12Java實(shí)現(xiàn)簡(jiǎn)單的斗地主游戲
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的斗地主游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04springboot配置開發(fā)和測(cè)試環(huán)境并添加啟動(dòng)路徑方式
這篇文章主要介紹了springboot配置開發(fā)和測(cè)試環(huán)境并添加啟動(dòng)路徑方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11