我的Java設計模式-模板方法模式

Jet啟思發表於2017-12-11

近日,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

AndroidJet的開發之路

相關文章