近日,ofo小黃車宣佈入駐法國巴黎,正式進入全球第20個國家,共享單車已然改變了我們的出行方式。就拿我自己來說,每當下班出地鐵的第一件事,以光速鎖定一輛共享單車,百米衝刺的速度搶在別人之前佔領它。
而大家都是重複著同樣的動作,拿出手機開鎖、騎車、上鎖、結算,哇~這是何等壯觀的場景,甚至還有的不用開鎖直接把車騎走的,鎖壞了嘛。
為什麼要用模板方法模式
現在共享單車以開鎖的方式來分,一般有掃碼開鎖和密碼開鎖兩種,來看共享單車使用流程的實現。
正常的思維邏輯是,抽象一個父類,子類繼承父類並實現父類方法。OK,看抽象類程式碼:
public abstract class AbstractClass {
// 開鎖
public abstract void unlock();
// 騎行
public abstract void ride();
// 上鎖
public abstract void lock();
// 結算
public abstract void pay();
// 使用者使用
public abstract void use();
}
複製程式碼
抽象類定義了我們使用共享單車的幾個基本流程,現在有兩種不同開鎖方式單車的使用,都繼承抽象類,程式碼如下:
//
public class ScanBicycle extends AbstractClass {
@Override
public void unlock() {
System.out.println("掃碼開鎖");
}
@Override
public void ride() {
System.out.println("騎起來很拉風");
}
@Override
public void lock() {
System.out.println("上鎖");
}
@Override
public void pay() {
System.out.println("結算");
}
@Override
public void use() {
unlock();
ride();
lock();
pay();
}
}
複製程式碼
以上是通過掃碼方式開鎖騎行,再來看密碼開鎖騎行,程式碼如下:
public class CodeBicycle extends AbstractClass {
@Override
public void unlock() {
System.out.println("密碼開鎖");
}
@Override
public void ride() {
System.out.println("騎起來很拉風");
}
@Override
public void lock() {
System.out.println("上鎖");
}
@Override
public void pay() {
System.out.println("結算");
}
@Override
public void use() {
unlock();
ride();
lock();
pay();
}
}
複製程式碼
好了,兩種方式都定義好了,看客戶端的呼叫:
public class Client {
public static void main(String[] args) {
ScanBicycle scanBicycle = new ScanBicycle();
scanBicycle.use();
System.out.println("========================");
CodeBicycle codeBicycle = new CodeBicycle();
codeBicycle.use();
}
}
複製程式碼
結果如下:
掃碼開鎖
騎起來很拉風
上鎖
結算
========================
掃碼開鎖
騎起來很拉風
上鎖
結算
相信都已經看出程式碼的問題,use方法的實現是一樣的,也就是程式碼重複了,這是病必須得治,藥方就是模板方式模式。
模板方法模式
定義
定義抽象類並且宣告一些抽象基本方法供子類實現不同邏輯,同時在抽象類中定義具體方法把抽象基本方法封裝起來,這就是模板方法模式。
UML
模板方法模式涉及到的角色有兩個角色:
- 抽象模板角色:定義一組基本方法供子類實現,定義並實現組合了基本方法的模板方法。
- 具體模板角色:實現抽象模板角色定義的基本方法
模板方法模式還涉及到以下方法的概念:
基本方法
-
抽象方法:由抽象模板角色宣告,abstract修飾,具體模板角色實現。
-
鉤子方法:由抽象模板角色宣告並實現,具體模板角色可實現加以擴充套件。
-
具體方法:由抽象模板角色宣告並實現,而子類並不實現。
模板方法
抽象模板角色宣告並實現,負責對基本方法的排程,一般以final修飾,不允許具體模板角色重寫。模板方法一般也是一個具體方法。
模式實戰
利用模板方式模式對上面的程式碼進行重構,來看抽象模板角色,程式碼如下:
public abstract class AbstractClass {
protected boolean isNeedUnlock = true; // 預設需要開鎖
/**
* 基本方法,子類需要實現
*/
protected abstract void unlock();
/**
* 基本方法,子類需要實現
*/
protected abstract void ride();
/**
* 鉤子方法,子類可實現
*
* @param isNeedUnlock
*/
protected void isNeedUnlock(boolean isNeedUnlock) {
this.isNeedUnlock = isNeedUnlock;
}
/**
* 模板方法,負責排程基本方法,子類不可實現
*/
public final void use() {
if (isNeedUnlock) {
unlock();
} else {
System.out.println("========鎖壞了,不用解鎖========");
}
ride();
}
}
複製程式碼
抽象模板角色定義了unlock和ride兩個使用單車的基本方法,還有一個鉤子方法,用來控制模板方法邏輯順序,核心是use模板方法,用final修飾,該方法完成對基本方法排程。注意,模板方法中對基本方法的排程是有順序有規則的。還有一點,基本方法都是protected修飾的,因為基本方法都是在以public修飾的模板方法中呼叫,並且可以由子類實現,並不需要暴露給其他類呼叫。
現在來看兩個具體模板角色的實現:
// 掃碼開鎖的單車
public class ScanBicycle extends AbstractClass {
@Override
protected void unlock() {
System.out.println("========" + "掃碼開鎖" + "========");
}
@Override
protected void ride() {
System.out.println(getClass().getSimpleName() + "騎起來很拉風");
}
protected void isNeedUnlock(boolean isNeedUnlock) {
this.isNeedUnlock = isNeedUnlock;
}
}
// 密碼開鎖的單車
public class CodeBicycle extends AbstractClass {
@Override
protected void unlock() {
System.out.println("========" + "密碼開鎖" + "========");
}
@Override
protected void ride() {
System.out.println(getClass().getSimpleName() + "騎起來很拉風");
}
protected void isNeedUnlock(boolean isNeedUnlock) {
this.isNeedUnlock = isNeedUnlock;
}
}
複製程式碼
可以看到,相比之前的實現,現在兩個具體類都不需要實現use方法,只負責實現基本方法的邏輯,職責上變得更加清晰了。來看使用者如何使用:
public class Client {
public static void main(String[] args) {
ScanBicycle scanBicycle = new ScanBicycle();
scanBicycle.use();
CodeBicycle codeBicycle = new CodeBicycle();
codeBicycle.use();
}
}
複製程式碼
執行結果如下:
========掃碼開鎖========
ScanBicycle騎起來很拉風
========密碼開鎖========
CodeBicycle騎起來很拉風
當我以百米衝刺的速度跑到共享單車面前時,發現這輛車的鎖是壞掉的,也就是不用開鎖,免費的,騎回家收藏也沒問題。在程式碼中只要呼叫鉤子方法isNeedUnlock就好,實現如下:
public class Client {
public static void main(String[] args) {
ScanBicycle scanBicycle = new ScanBicycle();
scanBicycle.isNeedUnlock(false);
scanBicycle.use();
CodeBicycle codeBicycle = new CodeBicycle();
codeBicycle.isNeedUnlock(true);
codeBicycle.use();
}
}
複製程式碼
執行結果如下:
========鎖壞了,不用解鎖========
ScanBicycle騎起來很拉風
========密碼開鎖========
CodeBicycle騎起來很拉風
上面提到模板方法對基本方法的排程是有順序的,也就是說模板方法中的邏輯是不可變的,子類只實現可以被實現的基本方法,但不會改變模板方法中的頂級邏輯。而鉤子方法的使用只是對模板方法中邏輯的控制,影響的是模板方法的結果,並不會改變原有邏輯。
模板方法模式的優缺點
優點
1)良好的封裝性。把公有的不變的方法封裝在父類,而子類負責實現具體邏輯。
2)良好的擴充套件性:增加功能由子類實現基本方法擴充套件,符合單一職責原則和開閉原則。
3)複用程式碼。
缺點
1)由於是通過繼承實現程式碼複用來改變演算法,靈活度會降低。
2)子類的執行影響父類的結果,增加程式碼閱讀難度。
總結
模板方法模式看上去簡單,但是整個模式涉及到的都是物件導向設計的核心,比如繼承封裝,基於繼承的程式碼複用,方法的實現等等。當中還有涉及到一些關鍵詞的使用,也是我們Java程式設計中需要掌握的基礎。總體來說,模板方法模式是很好的學習物件。下一篇是中介者模式,您的點贊和關注是我的動力,再會!
設計模式Java原始碼GitHub下載:https://github.com/jetLee92/DesignPattern