设计模式——15. 模板方法模式

1. 说明

模板方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的骨架,将算法的具体步骤延迟到子类中实现。这个模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。

模板方法模式基于以下核心思想:

  1. 定义一个抽象类,其中包含一个称为模板方法的方法。模板方法定义了一个算法的骨架,它由一系列步骤组成,其中的某些步骤是抽象的,留给子类去实现。
  2. 子类继承抽象类,并根据需要实现抽象步骤,完成算法的具体实现。
  3. 模板方法在运行时调用这些抽象步骤,确保算法的整体结构不变,但具体的步骤可以根据子类的实现而变化。
    模板方法模式的主要目标是促使代码重用和减少代码重复,因为算法的骨架在抽象类中已经定义好了,不同的实现可以在子类中复用相同的骨架。

2. 使用的场景

模板方法模式在以下情况下特别有用:

  1. 定义算法的框架和流程:当你需要定义一个算法的基本框架,但其中某些具体步骤的实现可能因情况而异时,可以使用模板方法模式。这种情况下,抽象类中的模板方法定义了算法的骨架,具体步骤由子类实现。
  2. 避免代码重复:当多个相关类具有相似的操作步骤,但某些步骤有所不同,你可以将共同的部分提取到抽象类的模板方法中,从而避免代码重复。
  3. 实现“开闭原则”:模板方法模式有助于实现开闭原则,因为它允许你在不修改算法的整体结构的情况下扩展或修改算法的特定步骤。新的子类可以轻松添加到系统中,而不会影响现有的算法。
  4. 控制算法的流程:模板方法允许你在抽象类中定义算法的流程,确保算法的执行顺序固定不变。这对于确保特定的操作顺序或规范性非常有用。
  5. 框架和库的设计:在框架和库的设计中,模板方法模式经常用于定义通用的工作流程,以便客户端代码可以根据需要进行自定义。例如,GUI 框架、数据库访问库和游戏引擎等都可以使用模板方法模式来定义通用的流程。

总之,模板方法模式适用于任何需要定义算法框架、控制流程并支持代码重用的情况。它使代码更具可维护性和可扩展性,并有助于遵循设计原则如开闭原则、单一职责原则等。

3. Python应用例子

以下是一个使用 Python 实现的简单示例,演示了模板方法模式。这个示例模拟了制作咖啡和茶的过程,其中咖啡和茶共享相同的制作步骤,但具体的实现细节不同。

from abc import ABC, abstractmethod

# 抽象类定义模板方法
class CaffeineBeverage(ABC):

    # 模板方法,定义了算法的骨架
    def prepare_beverage(self):
        self.boil_water()
        self.brew()
        self.pour_in_cup()
        self.add_condiments()

    # 具体步骤1:烧水
    def boil_water(self):
        print("Boiling water")

    # 具体步骤2:冲泡
    @abstractmethod
    def brew(self):
        pass

    # 具体步骤3:倒入杯中
    def pour_in_cup(self):
        print("Pouring into cup")

    # 具体步骤4:加调料(抽象方法,由子类实现)
    @abstractmethod
    def add_condiments(self):
        pass

# 具体子类1:制作咖啡
class Coffee(CaffeineBeverage):

    # 实现抽象方法:冲泡咖啡
    def brew(self):
        print("Dripping Coffee through filter")

    # 实现抽象方法:加糖和牛奶
    def add_condiments(self):
        print("Adding Sugar and Milk")

# 具体子类2:制作茶
class Tea(CaffeineBeverage):

    # 实现抽象方法:浸泡茶叶
    def brew(self):
        print("Steeping the tea")

    # 实现抽象方法:加柠檬
    def add_condiments(self):
        print("Adding Lemon")

if __name__ == "__main__":
    print("Making Coffee:")
    coffee = Coffee()
    coffee.prepare_beverage()

    print("\nMaking Tea:")
    tea = Tea()
    tea.prepare_beverage()

在这个示例中,CaffeineBeverage 是抽象类,它定义了模板方法 prepare_beverage,其中包括了制作咖啡和茶的共同步骤。具体子类 Coffee 和 Tea 继承自抽象类,并实现了抽象方法 brew 和 add_condiments,以便为咖啡和茶提供具体的制作步骤。

在主程序中,我们创建了 Coffee 和 Tea 的实例,并调用 prepare_beverage 方法来制作咖啡和茶。尽管它们共享相同的制作框架,但具体的实现步骤不同,这正是模板方法模式的应用。

4. 实现要素

模板方法模式的实现要素包括以下部分:

  1. 抽象类(Abstract Class):抽象类定义了算法的框架,它包括一个或多个模板方法以及零个或多个抽象方法。模板方法用于定义算法的骨架,抽象方法用于在子类中实现具体的步骤。
  2. 具体子类(Concrete Subclasses):具体子类继承自抽象类,并实现了其中的抽象方法,以完成算法的具体步骤。每个具体子类可以根据需要重写抽象方法,但不能修改模板方法的结构。

5. UML图

下面是一个模板方法模式的 UML 类图示例:

               +------------------+
               |   AbstractClass  |
               +------------------+
               | +templateMethod()|
               | +abstractMethod()|
               +------------------+
                        ^
                        |
           +------------|------------+
           |            |            |
+-----------------+ +-----------------+
| ConcreteClass1 | | ConcreteClass2 |
+-----------------+ +-----------------+
| +abstractMethod()| | +abstractMethod()|
+-----------------+ +-----------------+

在 UML 类图中,有一个抽象类 AbstractClass,它包含了一个模板方法 templateMethod 和一个抽象方法 abstractMethod。具体子类 ConcreteClass1 和 ConcreteClass2 继承自抽象类,并分别实现了 abstractMethod。

模板方法模式的核心是模板方法,它定义了算法的骨架,包括一组步骤,其中某些步骤可以由子类重写以实现不同的行为。具体的步骤是由抽象方法完成的,这些抽象方法必须在子类中实现。

6. Java/golang/javascrip/C++ 等语言实现方式

6.1 Java实现

上述例子用Java语言实现示例如下:

// 抽象类定义模板方法
abstract class CaffeineBeverage {

    // 模板方法,定义了算法的骨架
    final void prepareBeverage() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // 具体步骤1:烧水
    void boilWater() {
        System.out.println("Boiling water");
    }

    // 具体步骤2:冲泡(抽象方法,由子类实现)
    abstract void brew();

    // 具体步骤3:倒入杯中
    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 具体步骤4:加调料(抽象方法,由子类实现)
    abstract void addCondiments();
}

// 具体子类1:制作咖啡
class Coffee extends CaffeineBeverage {

    // 实现抽象方法:冲泡咖啡
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    // 实现抽象方法:加糖和牛奶
    void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
}

// 具体子类2:制作茶
class Tea extends CaffeineBeverage {

    // 实现抽象方法:浸泡茶叶
    void brew() {
        System.out.println("Steeping the tea");
    }

    // 实现抽象方法:加柠檬
    void addCondiments() {
        System.out.println("Adding Lemon");
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Making Coffee:");
        Coffee coffee = new Coffee();
        coffee.prepareBeverage();

        System.out.println("\nMaking Tea:");
        Tea tea = new Tea();
        tea.prepareBeverage();
    }
}

在这个 Java 示例中,CaffeineBeverage 是抽象类,它定义了模板方法 prepareBeverage,包括了制作咖啡和茶的共同步骤。具体子类 Coffee 和 Tea 继承自抽象类,并实现了抽象方法 brew 和 addCondiments,以提供咖啡和茶的具体制作步骤。

在主程序中,我们创建了 Coffee 和 Tea 的实例,并调用 prepareBeverage 方法来制作咖啡和茶。同样,尽管它们共享相同的制作框架,但具体的实现步骤不同,这正是模板方法模式的应用。

6.2 Golang实现

上述例子用golang实现示例如下:

package main

import "fmt"

// 抽象类定义模板方法
type CaffeineBeverage interface {
        prepareBeverage()
        boilWater()
        brew()
        pourInCup()
        addCondiments()
}

// 具体子类1:制作咖啡
type Coffee struct{}

func (c *Coffee) prepareBeverage() {
        c.boilWater()
        c.brew()
        c.pourInCup()
        c.addCondiments()
}

func (c *Coffee) boilWater() {
        fmt.Println("Boiling water")
}

func (c *Coffee) brew() {
        fmt.Println("Dripping Coffee through filter")
}

func (c *Coffee) pourInCup() {
        fmt.Println("Pouring into cup")
}

func (c *Coffee) addCondiments() {
        fmt.Println("Adding Sugar and Milk")
}

// 具体子类2:制作茶
type Tea struct{}

func (t *Tea) prepareBeverage() {
        t.boilWater()
        t.brew()
        t.pourInCup()
        t.addCondiments()
}

func (t *Tea) boilWater() {
        fmt.Println("Boiling water")
}

func (t *Tea) brew() {
        fmt.Println("Steeping the tea")
}

func (t *Tea) pourInCup() {
        fmt.Println("Pouring into cup")
}

func (t *Tea) addCondiments() {
        fmt.Println("Adding Lemon")
}

func main() {
        fmt.Println("Making Coffee:")
        coffee := &Coffee{}
        coffee.prepareBeverage()

        fmt.Println("\nMaking Tea:")
        tea := &Tea{}
        tea.prepareBeverage()
}

在这个 Go 示例中,我们定义了一个 CaffeineBeverage 接口来表示模板方法,其中包括了制作咖啡和茶的共同步骤。然后,我们创建了两个具体的结构体类型 Coffee 和 Tea,它们分别实现了 CaffeineBeverage 接口的方法来完成咖啡和茶的制作步骤。

在主程序中,我们创建了 Coffee 和 Tea 的实例,并调用 prepareBeverage 方法来制作咖啡和茶。同样,尽管它们共享相同的制作框架,但具体的实现步骤不同,这正是模板方法模式的应用。

6.3 Javascript实现

上述例子用javascript实现示例如下:

// 抽象类定义模板方法
class CaffeineBeverage {
  prepareBeverage() {
    this.boilWater();
    this.brew();
    this.pourInCup();
    this.addCondiments();
  }

  // 具体步骤1:烧水
  boilWater() {
    console.log("Boiling water");
  }

  // 具体步骤2:冲泡(抽象方法,由子类实现)
  brew() {}

  // 具体步骤3:倒入杯中
  pourInCup() {
    console.log("Pouring into cup");
  }

  // 具体步骤4:加调料(抽象方法,由子类实现)
  addCondiments() {}
}

// 具体子类1:制作咖啡
class Coffee extends CaffeineBeverage {
  // 实现抽象方法:冲泡咖啡
  brew() {
    console.log("Dripping Coffee through filter");
  }

  // 实现抽象方法:加糖和牛奶
  addCondiments() {
    console.log("Adding Sugar and Milk");
  }
}

// 具体子类2:制作茶
class Tea extends CaffeineBeverage {
  // 实现抽象方法:浸泡茶叶
  brew() {
    console.log("Steeping the tea");
  }

  // 实现抽象方法:加柠檬
  addCondiments() {
    console.log("Adding Lemon");
  }
}

console.log("Making Coffee:");
const coffee = new Coffee();
coffee.prepareBeverage();

console.log("\nMaking Tea:");
const tea = new Tea();
tea.prepareBeverage();

在这个 JavaScript 示例中,我们定义了一个 CaffeineBeverage 类来表示模板方法,其中包括了制作咖啡和茶的共同步骤。然后,我们创建了两个具体的子类 Coffee 和 Tea,它们分别实现了 CaffeineBeverage 类的方法来完成咖啡和茶的制作步骤。

在主程序中,我们创建了 Coffee 和 Tea 的实例,并调用 prepareBeverage 方法来制作咖啡和茶。尽管它们共享相同的制作框架,但具体的实现步骤不同,这正是模板方法模式的应用。

6.4 C++实现

上述例子用C++实现如下:

#include <iostream>

// 抽象类定义模板方法
class CaffeineBeverage {
public:
    void prepareBeverage() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // 具体步骤1:烧水
    void boilWater() {
        std::cout << "Boiling water" << std::endl;
    }

    // 具体步骤2:冲泡(纯虚函数,由子类实现)
    virtual void brew() = 0;

    // 具体步骤3:倒入杯中
    void pourInCup() {
        std::cout << "Pouring into cup" << std::endl;
    }

    // 具体步骤4:加调料(纯虚函数,由子类实现)
    virtual void addCondiments() = 0;
};

// 具体子类1:制作咖啡
class Coffee : public CaffeineBeverage {
public:
    // 实现抽象方法:冲泡咖啡
    void brew() override {
        std::cout << "Dripping Coffee through filter" << std::endl;
    }

    // 实现抽象方法:加糖和牛奶
    void addCondiments() override {
        std::cout << "Adding Sugar and Milk" << std::endl;
    }
};

// 具体子类2:制作茶
class Tea : public CaffeineBeverage {
public:
    // 实现抽象方法:浸泡茶叶
    void brew() override {
        std::cout << "Steeping the tea" << std::endl;
    }

    // 实现抽象方法:加柠檬
    void addCondiments() override {
        std::cout << "Adding Lemon" << std::endl;
    }
};

int main() {
    std::cout << "Making Coffee:" << std::endl;
    Coffee coffee;
    coffee.prepareBeverage();

    std::cout << "\nMaking Tea:" << std::endl;
    Tea tea;
    tea.prepareBeverage();

    return 0;
}

在这个 C++ 示例中,我们定义了一个 CaffeineBeverage 类来表示模板方法,其中包括了制作咖啡和茶的共同步骤。然后,我们创建了两个具体的子类 Coffee 和 Tea,它们分别实现了 CaffeineBeverage 类的纯虚函数来完成咖啡和茶的制作步骤。

在 main 函数中,我们创建了 Coffee 和 Tea 的实例,并调用 prepareBeverage 方法来制作咖啡和茶。尽管它们共享相同的制作框架,但具体的实现步骤不同,这正是模板方法模式的应用。

7. 练习题

假如你正在开发一个游戏,其中有两种不同的角色:战士和法师。这两种角色都具有攻击和防御的行为,但具体的实现不同。使用模板方法模式来设计这两种角色的行为。

请根据以下要求完成:

  1. 创建一个抽象类 Character,它包含一个模板方法 performAction,该方法包括以下步骤:
  • 调用 prepareForBattle 方法。
  • 调用 attack 方法。
  • 调用 defend 方法。
  • 调用 celebrate 方法。
  1. 创建两个具体子类 Warrior 和 Mage,它们都继承自 Character 类并实现以下方法:
  • prepareForBattle:输出角色准备战斗的消息。
  • attack:输出角色进行攻击的消息。
  • defend:输出角色进行防御的消息。
  • celebrate:输出角色庆祝胜利的消息。
  1. 在主程序中,创建一个战士对象和一个法师对象,并调用它们的 performAction 方法以模拟它们的行为。

示例输出:

Warrior is preparing for battle.
Warrior is attacking the enemy.
Warrior is defending against the enemy's attack.
Warrior is celebrating the victory.

Mage is preparing for battle.
Mage is attacking the enemy.
Mage is defending against the enemy's attack.
Mage is celebrating the victory.

你可以在评论区里或者私信我回复您的答案,这样我或者大家都能帮你解答,期待着你的回复~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guohuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值