需求

【设计模式专题之模板方法模式】18-咖啡馆

题目描述

  • 小明喜欢品尝不同类型的咖啡,她发现每种咖啡的制作过程有一些相同的步骤,他决定设计一个简单的咖啡制作系统,使用模板方法模式定义咖啡的制作过程。系统支持两种咖啡类型:美式咖啡(American Coffee)和拿铁(Latte)。

  • 咖啡制作过程包括以下步骤:

    1. 研磨咖啡豆 Grinding coffee beans
    2. 冲泡咖啡 Brewing coffee
    3. 添加调料 Adding condiments
  • 其中,美式咖啡和拿铁的调料添加方式略有不同, 拿铁在添加调料时需要添加牛奶Adding milk

输入描述

  • 多行输入,每行包含一个数字,表示咖啡的选择(1 表示美式咖啡,2 表示拿铁)。

输出描述

  • 根据每行输入,输出制作咖啡的过程,包括咖啡类型和各个制作步骤,末尾有一个空行。

输入示例

1
2

输出示例

Making American Coffee:
Grinding coffee beans
Brewing coffee
Adding condiments

Making Latte:
Grinding coffee beans
Brewing coffee
Adding milk
Adding condiments

基本概念

模板方法模式(Template Method Pattern)是一种行为型设计模式, 它定义了一个算法的骨架,将**一些步骤的实现延迟到子类。**模板方法模式使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。【引用自大话设计第10章】

举个简单的例子,做一道菜通常都需要包含至少三步:

  • 准备食材
  • 亨饪过程
  • 上菜

不同菜品的亨饪过程是不一样的,但是我们可以先定义一个”骨架”,包含这三个步骤,亨饪过程的过程放到具体的炒菜类中去实现,这样,无论炒什么菜,都可以沿用相同的炒菜算法,只需在子类中实现具体的炒菜步骤,从而提高了代码的复用性。

基本结构

模板方法模式的基本结构包含以下两个角色:

image-20240104144137427

  • 模板类AbstractClass:由一个模板方法和若干个基本方法构成,模板方法定义了逻辑的骨架,按照顺序调用包含的基本方法,基本方法通常是一些抽象方法,这些方法由子类去实现。基本方法还包含一些具体方法,它们是算法的一部分但已经有默认实现,在具体子类中可以继承或者重写。
  • 具体类ConcreteClass:继承自模板类,实现了在模板类中定义的抽象方法,以完成算法中特定步骤的具体实现。

简易实现

模板方法模式的简单示例如下:

  1. 定义模板类,包含模板方法,定义了算法的骨架, 一般都加上final关键字,避免子类重写。
// 模板类
abstract class AbstractClass {
    // 模板方法,定义了算法的骨架
    public final void templateMethod() {
        step1();
        step2();
        step3();
    }

    // 抽象方法,由子类实现
    protected abstract void step1();
    protected abstract void step2();
    protected abstract void step3();
}
  1. 定义具体类, 实现模板类中的抽象方法
// 具体类
class ConcreteClass extends AbstractClass {
    @Override
    protected void step1() {
        System.out.println("Step 1 ");
    }

    @Override
    protected void step2() {
        System.out.println("Step 2 ");
    }

    @Override
    protected void step3() {
        System.out.println("Step 3");
    }
}
  1. 客户端实现
public class Main {
    public static void main(String[] args) {
        AbstractClass concreteTemplate = new ConcreteClass();
        // 触发整个算法的执行
        concreteTemplate.templateMethod();
    }
}

应用场景

模板方法模式将算法的不变部分被封装在模板方法中,而可变部分算法由子类继承实现,这样做可以很好的提高代码的复用性,但是当算法的框架发生变化时,可能需要修改模板类,这也会影响到所有的子类。

总体来说,当算法的整体步骤很固定,但是个别步骤在更详细的层次上的实现可能不同时,通常考虑模板方法模式来处理。在已有的工具和库中, Spring框架中的JdbcTemplate类使用了模板方法模式,其中定义了一些执行数据库操作的模板方法,具体的数据库操作由回调函数提供。而在Java的JDK源码中,AbstractList 类也使用了模板方法模式,它提供了一些通用的方法,其中包括一些模板方法。具体的列表操作由子类实现。

本题代码

import java.util.Scanner;

// 抽象类
abstract class CoffeeMakerTemplate {
    private String coffeeName; // 添加咖啡名称字段

    // 构造函数,接受咖啡名称参数
    public CoffeeMakerTemplate(String coffeeName) {
        this.coffeeName = coffeeName;
    }

    // 模板方法定义咖啡制作过程
    final void makeCoffee() {
        System.out.println("Making " + coffeeName + ":");
        grindCoffeeBeans();
        brewCoffee();
        addCondiments();
        System.out.println();
    }

    // 具体步骤的具体实现由子类提供
    abstract void grindCoffeeBeans();
    abstract void brewCoffee();

    // 添加调料的默认实现
    void addCondiments() {
        System.out.println("Adding condiments");
    }
}

// 具体的美式咖啡类
class AmericanCoffeeMaker extends CoffeeMakerTemplate {
    // 构造函数传递咖啡名称
    public AmericanCoffeeMaker() {
        super("American Coffee");
    }

    @Override
    void grindCoffeeBeans() {
        System.out.println("Grinding coffee beans");
    }

    @Override
    void brewCoffee() {
        System.out.println("Brewing coffee");
    }
}

// 具体的拿铁咖啡类
class LatteCoffeeMaker extends CoffeeMakerTemplate {
    // 构造函数传递咖啡名称
    public LatteCoffeeMaker() {
        super("Latte");
    }

    @Override
    void grindCoffeeBeans() {
        System.out.println("Grinding coffee beans");
    }

    @Override
    void brewCoffee() {
        System.out.println("Brewing coffee");
    }

    // 添加调料的特定实现
    @Override
    void addCondiments() {
        System.out.println("Adding milk");
        System.out.println("Adding condiments");
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNext()) {
            int coffeeType = scanner.nextInt();

            CoffeeMakerTemplate coffeeMaker = null; 

            if (coffeeType == 1) {
                coffeeMaker = new AmericanCoffeeMaker();
            } else if (coffeeType == 2) {
                coffeeMaker = new LatteCoffeeMaker();
            } else {
                System.out.println("Invalid coffee type");
                continue;
            }

            // 制作咖啡
            coffeeMaker.makeCoffee();
        }
    }
}

其他语言版本

Java

添加钩子函数,也可由用户来确定是否添加牛奶或其它配料。

import java.util.Scanner;

// 抽象类,定义咖啡制作的基本步骤
abstract class CoffeeModel {
    private String coffeeName;

    // 构造函数,接受咖啡名称参数
    public CoffeeModel(String coffeeName) {
        this.coffeeName = coffeeName;
    }

    protected abstract void grind();
    protected abstract void brew();
    protected abstract void addCondiments();
    
    // 添加其他调料可使用该类
    public void addThings(){};

    // 模板方法,定义咖啡制作的流程
    public final void createCoffeeTemplate() {
        System.out.println("Making " + coffeeName + ":");
        grind();
        brew();
        //根据情况,是否调用添加更多调料
        if (isAddThings()) {
            addThings(); 
        }
        addCondiments();
        System.out.println();
    }

    // 默认不添加其他调料。如牛奶等
    public boolean isAddThings() {
        return false;
    }
}

//美式咖啡类实现
class CreateAmericanCoffee extends CoffeeModel {
    public CreateAmericanCoffee() {
        super("American Coffee");
    }

    @Override
    protected void grind() {
        System.out.println("Grinding coffee beans");
    }

    @Override
    protected void brew() {
        System.out.println("Brewing coffee");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding condiments");
    }
    
    // 美式咖啡默认不添加其他调料,如牛奶等
    @Override
    public boolean isAddThings() {
        return false; 
    }
}

//拿铁类实现
class CreateLatte extends CoffeeModel {
    private boolean addThingsFlag = true;

    public CreateLatte() {
        super("Latte");
    }

    @Override
    protected void grind() {
        System.out.println("Grinding coffee beans");
    }

    @Override
    protected void brew() {
        System.out.println("Brewing coffee");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding condiments");
    }
    
    //需要添加调料,牛奶
    @Override
    public void addThings(){
        System.out.println("Adding milk");
    }
    
    // 拿铁默认添加牛奶
    @Override
    public boolean isAddThings() {
        return this.addThingsFlag; 
    }

    // 外部调用以改变是否添加牛奶的状态,钩子函数
    public void setAddThingsFlag(boolean flag) {
        this.addThingsFlag = flag;
    }
}

//客户端
public class Main {
    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            while (scanner.hasNextInt()) {
                int input = scanner.nextInt();
                CoffeeModel coffee;
                switch (input) {
                    case 1:
                        coffee = new CreateAmericanCoffee();
                        break;
                    case 2:
                        coffee = new CreateLatte();
                        break;
                    default:
                        System.out.println("无效选择,请输入1或2");
                        continue;
                }
                coffee.createCoffeeTemplate();
            }
        }
    }
}

C++

#include <iostream>
#include <string>
#include <memory>

// 抽象类
class CoffeeMakerTemplate {
private:
    std::string coffeeName;

public:
    // 构造函数,接受咖啡名称参数
    CoffeeMakerTemplate(const std::string& coffeeName) : coffeeName(coffeeName) {}

    // 模板方法定义咖啡制作过程
    virtual void makeCoffee() {
        std::cout << "Making " << coffeeName << ":\n";
        grindCoffeeBeans();
        brewCoffee();
        addCondiments();
        std::cout << '\n';
    }

    // 具体步骤的具体实现由子类提供
    virtual void grindCoffeeBeans() = 0;
    virtual void brewCoffee() = 0;

    // 添加调料的默认实现
    virtual void addCondiments() {
        std::cout << "Adding condiments\n";
    }
};

// 具体的美式咖啡类
class AmericanCoffeeMaker : public CoffeeMakerTemplate {
public:
    // 构造函数传递咖啡名称
    AmericanCoffeeMaker() : CoffeeMakerTemplate("American Coffee") {}

    void grindCoffeeBeans() override {
        std::cout << "Grinding coffee beans\n";
    }

    void brewCoffee() override {
        std::cout << "Brewing coffee\n";
    }
};

// 具体的拿铁咖啡类
class LatteCoffeeMaker : public CoffeeMakerTemplate {
public:
    // 构造函数传递咖啡名称
    LatteCoffeeMaker() : CoffeeMakerTemplate("Latte") {}

    void grindCoffeeBeans() override {
        std::cout << "Grinding coffee beans\n";
    }

    void brewCoffee() override {
        std::cout << "Brewing coffee\n";
    }

    // 添加调料的特定实现
    void addCondiments() override {
        std::cout << "Adding milk\n";
        std::cout << "Adding condiments\n";
    }
};

int main() {
    std::unique_ptr<CoffeeMakerTemplate> coffeeMaker;

    int coffeeType;
    while (std::cin >> coffeeType) {
        if (coffeeType == 1) {
            coffeeMaker = std::make_unique<AmericanCoffeeMaker>();
        } else if (coffeeType == 2) {
            coffeeMaker = std::make_unique<LatteCoffeeMaker>();
        } else {
            std::cout << "Invalid coffee type\n";
            continue;
        }

        // 制作咖啡
        coffeeMaker->makeCoffee();
    }

    return 0;
}

Python

from abc import ABC, abstractmethod

# 抽象类
class CoffeeMakerTemplate(ABC):
    # 构造函数,接受咖啡名称参数
    def __init__(self, coffee_name):
        self.coffee_name = coffee_name

    # 模板方法定义咖啡制作过程
    def make_coffee(self):
        print(f"Making {self.coffee_name}:")
        self.grind_coffee_beans()
        self.brew_coffee()
        self.add_condiments()
        print()

    # 具体步骤的具体实现由子类提供
    @abstractmethod
    def grind_coffee_beans(self):
        pass

    @abstractmethod
    def brew_coffee(self):
        pass

    # 添加调料的默认实现
    def add_condiments(self):
        print("Adding condiments")


# 具体的美式咖啡类
class AmericanCoffeeMaker(CoffeeMakerTemplate):
    # 构造函数传递咖啡名称
    def __init__(self):
        super().__init__("American Coffee")

    def grind_coffee_beans(self):
        print("Grinding coffee beans")

    def brew_coffee(self):
        print("Brewing coffee")


# 具体的拿铁咖啡类
class LatteCoffeeMaker(CoffeeMakerTemplate):
    # 构造函数传递咖啡名称
    def __init__(self):
        super().__init__("Latte")

    def grind_coffee_beans(self):
        print("Grinding coffee beans")

    def brew_coffee(self):
        print("Brewing coffee")

    # 添加调料的特定实现
    def add_condiments(self):
        print("Adding milk")
        print("Adding condiments")


# 客户端代码
if __name__ == "__main__":
    while True:
        try:
            coffee_type = int(input())

            coffee_maker = None

            if coffee_type == 1:
                coffee_maker = AmericanCoffeeMaker()
            elif coffee_type == 2:
                coffee_maker = LatteCoffeeMaker()
            else:
                print("Invalid coffee type")
                continue

            # 制作咖啡
            coffee_maker.make_coffee()

        except EOFError:
            break

Go

package main

import (
	"fmt"
	"os"
)

// 抽象类接口
type CoffeeMakerTemplate interface {
	MakeCoffee()
	GrindCoffeeBeans()
	BrewCoffee()
	AddCondiments()
}

// 具体的美式咖啡类
type AmericanCoffeeMaker struct {
	coffeeName string
}

// 构造函数传递咖啡名称
func NewAmericanCoffeeMaker() *AmericanCoffeeMaker {
	return &AmericanCoffeeMaker{coffeeName: "American Coffee"}
}

// 实现接口
func (a *AmericanCoffeeMaker) MakeCoffee() {
	fmt.Printf("Making %s:\n", a.coffeeName)
	a.GrindCoffeeBeans()
	a.BrewCoffee()
	a.AddCondiments()
	fmt.Println()
}

func (a *AmericanCoffeeMaker) GrindCoffeeBeans() {
	fmt.Println("Grinding coffee beans")
}

func (a *AmericanCoffeeMaker) BrewCoffee() {
	fmt.Println("Brewing coffee")
}

func (a *AmericanCoffeeMaker) AddCondiments() {
	fmt.Println("Adding condiments")
}

// 具体的拿铁咖啡类
type LatteCoffeeMaker struct {
	coffeeName string
}

// 构造函数传递咖啡名称
func NewLatteCoffeeMaker() *LatteCoffeeMaker {
	return &LatteCoffeeMaker{coffeeName: "Latte"}
}

// 实现接口
func (l *LatteCoffeeMaker) MakeCoffee() {
	fmt.Printf("Making %s:\n", l.coffeeName)
	l.GrindCoffeeBeans()
	l.BrewCoffee()
	l.AddCondiments()
	fmt.Println()
}

func (l *LatteCoffeeMaker) GrindCoffeeBeans() {
	fmt.Println("Grinding coffee beans")
}

func (l *LatteCoffeeMaker) BrewCoffee() {
	fmt.Println("Brewing coffee")
}

func (l *LatteCoffeeMaker) AddCondiments() {
	fmt.Println("Adding milk")
	fmt.Println("Adding condiments")
}

func main() {
	for {
		var coffeeType int
		if _, err := fmt.Scan(&coffeeType); err != nil {
			if err.Error() == "expected integer" || err.Error() == "EOF" {
				break
			}
			fmt.Println(err)
			os.Exit(1)
		}

		var coffeeMaker CoffeeMakerTemplate

		switch coffeeType {
		case 1:
			coffeeMaker = NewAmericanCoffeeMaker()
		case 2:
			coffeeMaker = NewLatteCoffeeMaker()
		default:
			fmt.Println("Invalid coffee type")
			continue
		}

		// 制作咖啡
		coffeeMaker.MakeCoffee()
	}
}