Template Method 模版方法模式

Intent 意圖

模板方法是一種行爲設計模式,它定義超類中算法的骨架,但允許子類在不改變其結構的情況下覆蓋算法的特定步驟。template-method-header

Problem 問題

假設您正在創建一個用於分析公司文檔的數據挖掘應用程序。用戶以各種格式(PDF、DOC、CSV)嚮應用程序提供文檔,並嘗試以統一格式從這些文檔中提取有意義的數據。

該應用程序的第一個版本只能與 DOC 文件一起使用。在以下版本中,它能夠支持 CSV 文件。一個月後,您 “教” 它從 PDF 文件中提取數據。

數據挖掘類包含大量重複代碼。

在某個時候,你注意到這三個類都有很多相似的代碼。雖然處理各種數據格式的代碼在所有類中都完全不同,但用於數據處理和分析的代碼幾乎相同。擺脫代碼重複,保持算法結構完好無損不是很好嗎?

還有另一個與使用這些類的客戶端代碼相關的問題。它有很多條件,這些條件根據處理對象的類選擇適當的操作過程。如果所有三個處理類都具有公共接口或基類,則可以在客戶端代碼中消除條件,並在對處理對象調用方法時使用多態性。

Solution 解決方案

模板方法模式建議將算法分解爲一系列步驟,將這些步驟轉換爲方法,並在單個模板方法中放置對這些方法的一系列調用。這些步驟可以是 模板方法模式建議將算法分解爲一系列步驟,將這些步驟轉換爲方法,並在單個模板方法中放置對這些方法的一系列調用。這些步驟可以是 abstract ,也可以具有一些默認實現。要使用該算法,客戶端應該提供自己的子類,實現所有抽象步驟,並在需要時覆蓋一些可選步驟(但不是模板方法本身)。 ,也可以具有一些默認實現。要使用該算法,客戶端應該提供自己的子類,實現所有抽象步驟,並在需要時覆蓋一些可選步驟(但不是模板方法本身)。

我們看看這將如何在我們的數據挖掘應用程序中發揮作用。我們可以爲所有三種解析算法創建一個基類。此類定義一個模板方法,該方法由對各種文檔處理步驟的一系列調用組成。

模板方法將算法分解爲多個步驟,允許子類覆蓋這些步驟,但不能覆蓋實際方法。

首先,我們可以聲明所有步驟 abstract ,迫使子類爲這些方法提供自己的實現。在我們的例子中,子類已經具有所有必要的實現,因此我們唯一需要做的就是調整方法的簽名以匹配超類的方法。

現在,讓我們看看我們可以做些什麼來擺脫重複的代碼。對於各種數據格式,打開 / 關閉文件和提取 / 解析數據的代碼似乎不同,因此沒有必要觸及這些方法。但是,其他步驟(例如分析原始數據和編寫報告)的實現非常相似,因此可以將其拉到基類中,子類可以在其中共享該代碼。

正如你所看到的,我們有兩種類型的步驟:

Real-World Analogy 真實世界的類比

典型的建築計劃可以稍作改動,以更好地滿足客戶的需求。

模板方法方法可用於大規模住房建設。建造標準房屋的建築計劃可能包含幾個擴展點,這些擴展點可以讓潛在所有者調整最終房屋的一些細節。

每個建築步驟,如奠基、框架、砌牆、安裝水管和水電佈線等,都可以稍作改動,使最終的房子與其他房子略有不同。

Structure 結構

  1. 1. Abstract 類聲明充當算法步驟的方法,以及按特定順序調用這些方法的實際模板方法。這些步驟可以聲明 Abstract 類聲明充當算法步驟的方法,以及按特定順序調用這些方法的實際模板方法。這些步驟可以聲明 abstract ,也可以具有一些默認實現。 ,也可以具有一些默認實現。

  2. 2. 具體類可以覆蓋所有步驟,但不能覆蓋模板方法本身。

Pseudocode 僞代碼

在此示例中,模板方法模式爲簡單策略視頻遊戲中的人工智能的各個分支提供了一個 “骨架”。

簡單視頻遊戲的 AI 類。

遊戲中的所有種族都有幾乎相同類型的單位和建築物。因此,您可以爲各種種族重複使用相同的 AI 結構,同時能夠覆蓋一些細節。通過這種方法,您可以覆蓋獸人的 AI 使其更具攻擊性,使人類更具防禦性,並使怪物無法建造任何東西。向遊戲添加新種族需要創建一個新的 AI 子類並覆蓋基 AI 類中聲明的默認方法。

// The abstract class defines a template method that contains a
// skeleton of some algorithm composed of calls, usually to
// abstract primitive operations. Concrete subclasses implement
// these operations, but leave the template method itself
// intact.
class GameAI is
    // The template method defines the skeleton of an algorithm.
    method turn() is
        collectResources()
        buildStructures()
        buildUnits()
        attack()

    // Some of the steps may be implemented right in a base
    // class.
    method collectResources() is
        foreach (s in this.builtStructures) do
            s.collect()

    // And some of them may be defined as abstract.
    abstract method buildStructures()
    abstract method buildUnits()

    // A class can have several template methods.
    method attack() is
        enemy = closestEnemy()
        if (enemy == null)
            sendScouts(map.center)
        else
            sendWarriors(enemy.position)

    abstract method sendScouts(position)
    abstract method sendWarriors(position)

// Concrete classes have to implement all abstract operations of
// the base class but they must not override the template method
// itself.
class OrcsAI extends GameAI is
    method buildStructures() is
        if (there are some resources) then
            // Build farms, then barracks, then stronghold.

    method buildUnits() is
        if (there are plenty of resources) then
            if (there are no scouts)
                // Build peon, add it to scouts group.
            else
                // Build grunt, add it to warriors group.

    // ...

    method sendScouts(position) is
        if (scouts.length > 0) then
            // Send scouts to position.

    method sendWarriors(position) is
        if (warriors.length > 5) then
            // Send warriors to position.

// Subclasses can also override some operations with a default
// implementation.
class MonstersAI extends GameAI is
    method collectResources() is
        // Monsters don't collect resources.

    method buildStructures() is
        // Monsters don't build structures.

    method buildUnits() is
        // Monsters don't build units.

Applicability 適用性

如果希望客戶端僅擴展算法的特定步驟,而不擴展整個算法或其結構,請使用模板方法模式。

模板方法允許您將單體算法轉換爲一系列單獨的步驟,這些步驟可以由子類輕鬆擴展,同時保持超類中定義的結構不變。

當您有多個類包含幾乎相同的算法但有一些細微差異時,請使用該模式。因此,當算法更改時,您可能需要修改所有類。

當您將此類算法轉換爲模板方法時,還可以將具有類似實現的步驟拉到超類中,從而消除代碼重複。子類之間不同的代碼可以保留在子類中。

How to Implement 如何實現

  1. 1. 分析目標算法,看看是否可以將其分解爲步驟。考慮哪些步驟對所有子類都是通用的,哪些步驟將始終是唯一的。

  2. 2. 創建抽象基類並聲明模板方法和一組表示算法步驟的抽象方法。通過執行相應的步驟,在模板方法中概述算法的結構。請考慮創建模板方法 final 以防止子類重寫它。

  3. 3. 如果所有步驟最終都是抽象的,那也沒關係。但是,某些步驟可能會受益於默認實現。子類不必實現這些方法。

  4. 4. 考慮在算法的關鍵步驟之間添加鉤子。

  5. 5. 對於算法的每個變體,創建一個新的具體子類。它必須實現所有抽象步驟,但也可以覆蓋一些可選步驟。

Pros and Cons 優點和缺點

xbXPfS

Relations with Other Patterns

與其他模式的關係

Template Method in Python

模板方法是一種行爲設計模式,它允許您在基類中定義算法的骨架,並讓子類在不更改整體算法結構的情況下覆蓋這些步驟。

main.py:概念示例

from abc import ABC, abstractmethod


class AbstractClass(ABC):
    """
    The Abstract Class defines a template method that contains a skeleton of
    some algorithm, composed of calls to (usually) abstract primitive
    operations.

    Concrete subclasses should implement these operations, but leave the
    template method itself intact.
    """

    def template_method(self) -> None:
        """
        The template method defines the skeleton of an algorithm.
        """

        self.base_operation1()
        self.required_operations1()
        self.base_operation2()
        self.hook1()
        self.required_operations2()
        self.base_operation3()
        self.hook2()

    # These operations already have implementations.

    def base_operation1(self) -> None:
        print("AbstractClass says: I am doing the bulk of the work")

    def base_operation2(self) -> None:
        print("AbstractClass says: But I let subclasses override some operations")

    def base_operation3(self) -> None:
        print("AbstractClass says: But I am doing the bulk of the work anyway")

    # These operations have to be implemented in subclasses.

    @abstractmethod
    def required_operations1(self) -> None:
        pass

    @abstractmethod
    def required_operations2(self) -> None:
        pass

    # These are "hooks." Subclasses may override them, but it's not mandatory
    # since the hooks already have default (but empty) implementation. Hooks
    # provide additional extension points in some crucial places of the
    # algorithm.

    def hook1(self) -> None:
        pass

    def hook2(self) -> None:
        pass


class ConcreteClass1(AbstractClass):
    """
    Concrete classes have to implement all abstract operations of the base
    class. They can also override some operations with a default implementation.
    """

    def required_operations1(self) -> None:
        print("ConcreteClass1 says: Implemented Operation1")

    def required_operations2(self) -> None:
        print("ConcreteClass1 says: Implemented Operation2")


class ConcreteClass2(AbstractClass):
    """
    Usually, concrete classes override only a fraction of base class'
    operations.
    """

    def required_operations1(self) -> None:
        print("ConcreteClass2 says: Implemented Operation1")

    def required_operations2(self) -> None:
        print("ConcreteClass2 says: Implemented Operation2")

    def hook1(self) -> None:
        print("ConcreteClass2 says: Overridden Hook1")


def client_code(abstract_class: AbstractClass) -> None:
    """
    The client code calls the template method to execute the algorithm. Client
    code does not have to know the concrete class of an object it works with, as
    long as it works with objects through the interface of their base class.
    """

    # ...
    abstract_class.template_method()
    # ...


if __name__ == "__main__":
    print("Same client code can work with different subclasses:")
    client_code(ConcreteClass1())
    print("")

    print("Same client code can work with different subclasses:")
    client_code(ConcreteClass2())

輸出. txt:執行結果

Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway

Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway

Template Method in Rust

模板方法是一種行爲設計模式,它允許您在基類中定義算法的骨架,並讓子類在不更改整體算法結構的情況下覆蓋這些步驟。

Conceptual Example 概念示例

main.rs

trait TemplateMethod {
    fn template_method(&self) {
        self.base_operation1();
        self.required_operations1();
        self.base_operation2();
        self.hook1();
        self.required_operations2();
        self.base_operation3();
        self.hook2();
    }

    fn base_operation1(&self) {
        println!("TemplateMethod says: I am doing the bulk of the work");
    }

    fn base_operation2(&self) {
        println!("TemplateMethod says: But I let subclasses override some operations");
    }

    fn base_operation3(&self) {
        println!("TemplateMethod says: But I am doing the bulk of the work anyway");
    }

    fn hook1(&self) {}
    fn hook2(&self) {}

    fn required_operations1(&self);
    fn required_operations2(&self);
}

struct ConcreteStruct1;

impl TemplateMethod for ConcreteStruct1 {
    fn required_operations1(&self) {
        println!("ConcreteStruct1 says: Implemented Operation1")
    }

    fn required_operations2(&self) {
        println!("ConcreteStruct1 says: Implemented Operation2")
    }
}

struct ConcreteStruct2;

impl TemplateMethod for ConcreteStruct2 {
    fn required_operations1(&self) {
        println!("ConcreteStruct2 says: Implemented Operation1")
    }

    fn required_operations2(&self) {
        println!("ConcreteStruct2 says: Implemented Operation2")
    }
}

fn client_code(concreteimpl TemplateMethod) {
    concrete.template_method()
}

fn main() {
    println!("Same client code can work with different concrete implementations:");
    client_code(ConcreteStruct1);
    println!();

    println!("Same client code can work with different concrete implementations:");
    client_code(ConcreteStruct2);
}

Output 輸出

Same client code can work with different concrete implementations:
TemplateMethod says: I am doing the bulk of the work
ConcreteStruct1 says: Implemented Operation1
TemplateMethod says: But I let subclasses override some operations
ConcreteStruct1 says: Implemented Operation2
TemplateMethod says: But I am doing the bulk of the work anyway

Same client code can work with different concrete implementations:
TemplateMethod says: I am doing the bulk of the work
ConcreteStruct2 says: Implemented Operation1
TemplateMethod says: But I let subclasses override some operations
ConcreteStruct2 says: Implemented Operation2
TemplateMethod says: But I am doing the bulk of the work anyway
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/eWJ8LeqvOvES9-DMeo3AkQ