前言
在上一篇文章責任鏈模式
中提到了模板方法模式,因此這裏簡單介紹一下.
模板方法模式比較簡單,或者說比較常用.在開發過程中,許多人在不知不覺的情況下就會使用,只要他具有良好的面對對象思維.
比如當你寫了Dog
和Cat
兩個類,發現很多相同的代碼,你自然就會將相同模塊提取抽象成父類,然後將一些公共的方法放到父類中,這樣子就基本實現了模板方式模式.
介紹(摘自《Head FIrst 設計模式》)
在一個方法中定義一個算法的骨架,而將一些詳細的步驟延遲到子類中.
模板方法使得子類可以在不改變算法結果的基礎上,重新定義算法中的某些步驟.
類圖
角色
抽象模板: 抽象模板一般有一個具體實現的方法,用來定義算法的基礎骨架.還有一些抽象方法留給子類去具體實現.此外還有一些有默認實現的鉤子方法.子類可選實現.
具體模板: 繼承父類的具體方法,實現他們的抽象方法,對於鉤子方法,可以根據自身情況決定是都重寫.
舉個栗子
書上的例子好多了,網絡上也有很多,我自己臨時瞎想一個吧,不保證一定合適.
假如我們現在要實現兩個類,Dog
和Cat
,並且實現他們的進攻方法.
我們大致實現以下,如下.
Cat:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Cat1 {
private void attack() {
prepared();
jump();
bite();
}
private void prepared() {
}
private void jump() {
}
private void bite() {
}
}
Dog:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Dog1 {
private void attack(){
prepared();
run();
shout();
bite();
}
private void prepared(){
}
private void run(){
}
private void shout(){
}
private void bite(){
}
}
仔細查看代碼,發現其實他們的攻擊過程很相似.
狗:準備,跑過去,發出聲音,咬住
貓:準備,跳過去,咬住
但是這樣子編碼的話,我們將相同的prepared
和bite
分別寫了兩次,這是不科學的.
很明顯我們可以實現一個父類,將prepared
和bite
在父類裏實現他們分別繼承就好了.
那麼對於shout
和jump/run
方法呢?就分別實現了嘛?
不是的,jump
和run
都是移動,只是實現方法不同,也是可以抽象到父類的,那麼shout
呢?這就是鉤子的作用了,可以動態控制當前動物是否會發出聲音之後再進行攻擊
.
改進後的代碼如下:
首先是動物模板類:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public abstract class AnimalTemplate {
protected boolean isShout;
public final void attack() {
//
prepared();
move();
if (isShout) {
shout();
}
bite();
}
private void prepared() {
//具體實現準備方案
System.out.println("準備");
}
abstract void move();
private void bite() {
//具體實現咬的方式
System.out.println("咬");
}
abstract void shout();
public void setShout() {
isShout = true;
}
}
注意,代碼中的setShout
是鉤子方法,這裏簡單的用一個變量來當做鉤子,此外,prepared
和bite
是具體方法,而move
和shout
爲抽象方法.
下面是狗的具體實現:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Dog extends AnimalTemplate {
@Override
void move() {
System.out.println("我是狗,我跑過去");
}
@Override
void shout() {
System.out.println("我是狗,我叫一下,嚇唬嚇唬他");
}
}
貓的具體實現:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Cat extends AnimalTemplate {
@Override
void move() {
System.out.println("我是貓,我跳過去.");
}
@Override
void shout() {
System.out.println("我是貓,我不叫.");
}
@Override
public void setShout() {
this.isShout = false;
}
}
測試類:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Test {
public static void main(String[] args) {
AnimalTemplate dog = new Dog();
dog.setShout();
dog.attack();
AnimalTemplate cat = new Cat();
cat.setShout();
cat.attack();
}
}
輸出結果:
準備
我是狗,我跑過去
我是狗,我叫一下,嚇唬嚇唬他
咬
準備
我是貓,我跳過去.
咬
對比上下的代碼可以發現,在第二個版本中,沒有一些重複的代碼,且子類的邏輯更加清晰,僅僅實現了自己與其他類不相同的部分,或者自己想要實現的部分鉤子方法.
而且當動物越來越多,代碼的總量會越來越少且容易維護,新添加一個動物,只需要繼承動物模板,然後實現move
和shout
即可.
總結
首先注意一下:在模板方法的attack
上,添加了final
關鍵字,可以防止該方法被重寫,可以保證attack
這一方法,在定義及流程上的正確性及安全性,而具體的實現可以交給子類.
模板方法的優點:
1、封裝不變部分,擴展可變部分。
2、提取公共代碼,便於維護。
3、行爲由父類控制,子類實現。
缺點:
類的個數較多.
完。
ChangeLog
2019-03-20 完成以上皆爲個人所思所得,如有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文鏈接。
聯繫郵箱:[email protected]
更多學習筆記見個人博客------>呼延十