模板方法模式是什麼?
定義一個方法,這個方法不能被子類複寫 但是可以被子類調用。在這個方法裏規定了算法流程或者方法調用順序。按照這種想法實現的就叫模板方法模式。
案例展示
說總是模糊,做給你看更清晰!
假設咱們來總結一下貝爺喫昆蟲的節目,然後用模板方法模式做個小案例。
我們來分析一下貝爺,喫昆蟲的流程:
- 抓蟲子
- 去頭
- 去內臟
- 一口吞
- 點評一波
下邊在咱們就按照這個模板來做個模板方法小案例
昆蟲——父類、模板方法所在類
package 模板方法模式;
public abstract class 昆蟲 {
/**
* 模板方法,喫昆蟲,模板方法做成final,不讓子類複寫
*/
final void eatInsect() {
grab();
decapitation();
gutted();
eat();
evaluation();
}
/**
* 抓蟲子
* 抽象方法,具體由子類實現(抓什麼蟲子)
*/
abstract void grab();
/**
* 去頭
*/
void decapitation() {
System.out.println("去頭");
}
/**
* 去除內臟
*/
void gutted() {
System.out.println("擠出內臟");
}
void eat() {
System.out.println("一口吞下");
}
/**
* 喫完蟲子後的評價——抽象方法
*/
abstract void evaluation();
}
然後寫具體的子類
螞蚱
package 模板方法模式;
public class 螞蚱 extends 昆蟲{
@Override
void grab() {
System.out.println("貝爺,抓住了一個螞蚱!");
}
@Override
void evaluation() {
System.out.println("味道像雞肉,還不錯!");
}
}
天花幼蟲
package 模板方法模式;
public class 天牛幼蟲 extends 昆蟲{
@Override
void grab() {
System.out.println("貝爺,捉到一隻天牛幼蟲!");
}
@Override
void evaluation() {
System.out.println("這條蟲,是我喫過最難喫的東西之一");
}
}
接下來開始重點戲:貝爺開始喫蟲
貝爺
package 模板方法模式;
public class 貝爺 {
public static void main(String[] args) {
System.out.println("荒野求生節目,現在開始!");
System.out.println("----------");
昆蟲 grasshopper = new 螞蚱();
grasshopper.eatInsect();
System.out.println("----------");
昆蟲 beetleLarvae = new 天牛幼蟲();
beetleLarvae.eatInsect();
}
}
運行結果:
荒野求生節目,現在開始!
----------
貝爺,抓住了一個螞蚱!
去頭
擠出內臟
一口吞下
味道像雞肉,還不錯!
----------
貝爺,捉到一隻天牛幼蟲!
去頭
擠出內臟
一口吞下
這條蟲,是我喫過最難喫的東西之一
優點分析:
我們可以看到上邊定義了喫蟲子方法的模板後,具體到某個蟲子,只需要再寫一點點代碼,然後調用模板方法就可以了。
缺點分析:
他的優勢也是他的弱勢,當子類具體方法各不相同時,寫這個模板不僅沒用還麻煩。
代碼問題:
經常看荒野求生的人應該知道,貝爺喫過的蟲子還有蚯蚓!!沒有去頭!沒有擠出內臟,那怎麼辦呢?
使用鉤子函數,其實就是調用方法時加一個** if **的事,true就條用,false就不調用。
下面咱們改善一下代碼。
改善版:
昆蟲:
package 模板方法模式.improve;
public abstract class 昆蟲 {
/**
* 模板方法,喫昆蟲,模板方法做成final,不讓子類複寫
*/
final void eatInsect() {
grab();
if(needDecapitation()) {
decapitation();
}
if(needGutted()) {
gutted();
}
eat();
evaluation();
}
/**
* 鉤子函數,確定是否需要去頭
* @return
*/
public boolean needDecapitation() {
return true;
}
/**
* 鉤子函數,決定是否需要去內臟
* @return
*/
public boolean needGutted() {
return true;
}
/**
* 抓蟲子
* 抽象方法,具體由子類實現(抓什麼蟲子)
*/
abstract void grab();
/**
* 去頭
*/
void decapitation() {
System.out.println("去頭");
}
/**
* 去除內臟
*/
void gutted() {
System.out.println("擠出內臟");
}
void eat() {
System.out.println("一口吞下");
}
/**
* 喫完蟲子後的評價——抽象方法
*/
abstract void evaluation();
}
因爲鉤子函數默認返回的是true,所以在螞蚱、天牛幼蟲實現時,不用做任何改變。而蚯蚓就需要複寫一下鉤子函數的代碼了。
蚯蚓
package 模板方法模式.improve;
public abstract class 昆蟲 {
/**
* 模板方法,喫昆蟲,模板方法做成final,不讓子類複寫
*/
final void eatInsect() {
grab();
if(needDecapitation()) {
decapitation();
}
if(needGutted()) {
gutted();
}
eat();
evaluation();
}
/**
* 鉤子函數,確定是否需要去頭
* @return
*/
public boolean needDecapitation() {
return true;
}
/**
* 鉤子函數,決定是否需要去內臟
* @return
*/
public boolean needGutted() {
return true;
}
/**
* 抓蟲子
* 抽象方法,具體由子類實現(抓什麼蟲子)
*/
abstract void grab();
/**
* 去頭
*/
void decapitation() {
System.out.println("去頭");
}
/**
* 去除內臟
*/
void gutted() {
System.out.println("擠出內臟");
}
void eat() {
System.out.println("一口吞下");
}
/**
* 喫完蟲子後的評價——抽象方法
*/
abstract void evaluation();
}
再讓貝爺喫一波看看
貝爺
package 模板方法模式.improve;
public class 貝爺 {
public static void main(String[] args) {
System.out.println("荒野求生節目,現在開始!");
System.out.println("----------");
昆蟲 grasshopper = new 螞蚱();
grasshopper.eatInsect();
System.out.println("----------");
昆蟲 beetleLarvae = new 天牛幼蟲();
beetleLarvae.eatInsect();
System.out.println("----------");
昆蟲 earthworm = new 蚯蚓();
earthworm.eatInsect();
}
}
運行結果:
荒野求生節目,現在開始!
----------
貝爺,抓住了一個螞蚱!
去頭
擠出內臟
一口吞下
味道像雞肉,還不錯!
----------
貝爺,捉到一隻天牛幼蟲!
去頭
擠出內臟
一口吞下
這條蟲,是我喫過最難喫的東西之一
----------
貝爺,抓住了一條蚯蚓!
一口吞下
這玩意,味道出乎意料,有點像意大利麪
優點:
通過鉤子函數我們可以控制一些方法是否調用。增加了可用性。
缺點:
當需要變化的太多時,如果每個都需要加鉤子函數,那就是去了模板的意義。