模版方法模式
我先問個問題,把大象放冰箱,總共分幾步?你一定脫口而出:三步!第一步把冰箱門打開,第二步把大象放進去,第三部把冰箱門關(guān)上。這道題是不是太簡單了?但當我們考慮細節(jié)的時候就沒這么簡單了。
假如你的冰箱門加了鎖,那么第一步開門時就需要開鎖。第二步把大象放進去也有細節(jié)要考慮。如果你的冰箱是臥式的,那么大象需要躺在里面。如果你的冰箱是立式的,那么大象可以站在里面。像這種主體邏輯一樣,細節(jié)實現(xiàn)不同的場景,我們可以考慮使用模版方法模式。
在軟件建模中,把大象放冰箱是一個算法,我們將其定義為一個模版方法。模版方法用一系列抽象的操作定義一個算法。就像我們例子中的三步,打開冰箱、大象放進去、關(guān)上冰箱門。具體如何開門和關(guān)門,如何放大象進去,則在子類中實現(xiàn)。冰箱不同,采用的方式自然也不同。
模版方法中定義操作的方法和先后步驟。而真正的操作方法實現(xiàn)則在子類中。
1. 實現(xiàn)模版方法
我們看看用代碼如何實現(xiàn)把大象放冰箱。為了便于理解,我們盡量少引入類,我們假設(shè)冰箱自身有個行為是把大象放進來
冰箱抽象類 Fridge
:
public abstract class Fridge {
public void placeElephant(){
System.out.println("開始裝大象");
openDoor();
putElephant();
closeDoor();
System.out.println("結(jié)束");
}
public abstract void openDoor();
public abstract void putElephant();
public abstract void closeDoor();
}
Fridge
類中定義了一個模版方法 placeELephan, 里面按照順序調(diào)用 openDoor、putELephant、closeDoor。這三個方法留待子類實現(xiàn)。
下面是兩個具體的冰箱實現(xiàn)類代碼。
立式冰箱類 VerticalFridge
:
public class VerticalFridge extends Fridge{
@Override
public void openDoor() {
System.out.println("打開立式冰箱門");
}
@Override
public void putElephant() {
System.out.println("將大象站著放進去");
}
@Override
public void closeDoor() {
System.out.println("關(guān)上立式冰箱門");
}
}
臥式冰箱 HorizontalFridge
:
public class HorizontalFridge extends Fridge{
@Override
public void openDoor() {
System.out.println("打開臥式冰箱門");
}
@Override
public void putElephant() {
System.out.println("將大象躺著放進去");
}
@Override
public void closeDoor() {
System.out.println("關(guān)上臥式冰箱門");
}
}
兩個冰箱子類各自實現(xiàn)三個抽象方法。
客戶端代碼:
public class Client {
public static void main(String[] args) {
Fridge fridge;
fridge = new HorizontalFridge();
fridge.placeElephant();
System.out.println("---------I'm a line-----------");
fridge = new VerticalFridge();
fridge.placeElephant();
}
}
客戶端代碼中,兩個不同的子類分表調(diào)用了placeElephant 方法,輸出如下:
開始裝大象
打開臥式冰箱門
將大象躺著放進去
關(guān)上臥式冰箱門
結(jié)束
---------I'm a line-----------
開始裝大象
打開立式冰箱門
將大象站著放進去
關(guān)上立式冰箱門
結(jié)束
兩個子類開始和結(jié)束的步驟一樣,中間步驟的順序也一樣。這些邏輯在父類中實現(xiàn)。每個步驟具體的邏輯則在子類中各自實現(xiàn)。
類圖:
2. 模版方法優(yōu)缺點
2.1 優(yōu)點
分離了算法中變和不變的部分。不變的部分定義在父類的模版方法中。變的部分通過子類實現(xiàn)。不變的算法部分可以被充分復用。當變的部分有新需求時,可以定義新的子類。從而實現(xiàn)了開閉原則。
2.2 缺點
模版意味著死板,我們設(shè)定好模版就必須按照模版的一、二、三步來執(zhí)行。如果我們想調(diào)換順序,或者增加幾步就很難做到。除非定義新的模版?;蛘吆苄⌒牡母膭右延心0?,避免影響現(xiàn)有程序邏輯。但這已經(jīng)違反了開閉原則。
3. 模版方法適用場景
如果我們發(fā)現(xiàn)一系列的算法,主干一樣,只是在局部的實現(xiàn)上有區(qū)別。此時我們可以考慮使用模版方法。把算法主干及不變的部分提煉出來,在父類中實現(xiàn)。抽象出變化部分的方法,交由不同的子類自己去實現(xiàn)。
4.小結(jié)
一般來說我們都是在子類中調(diào)用父類的方法。而模版方法恰恰相反,是父類的方法中調(diào)用子類的實現(xiàn)。正是因為這樣,我們才能把不變的行為抽象到父類中,變化的部分留給子類實現(xiàn)。此外還有一類方法叫做鉤子方法,它不是抽象的方法,父類有其缺省實現(xiàn),一般是空方法。但是子類也可以通過重寫去覆蓋。