对模板方法模式的理解

对模板方法模式的理解

一、场景

1、题目【来源

1.1 题目描述

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

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

  1. 研磨咖啡豆 Grinding coffee beans
  2. 冲泡咖啡 Brewing coffee
  3. 添加调料 Adding condiments

其中,美式咖啡和拿铁的调料添加方式略有不同, 拿铁在添加调料时需要添加牛奶Adding milk

1.2 输入描述

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

1.3 输出描述

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

1.4 输入示例
复制代码
1
2
1.5 输出示例
复制代码
Making American Coffee:
Grinding coffee beans
Brewing coffee
Adding condiments

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

二、不采用模板方法模式

1、代码

java 复制代码
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNextInt()) {
            int n = scanner.nextInt();

            if (n == 1) {
                System.out.println("Making American Coffee:");
                System.out.println("Grinding coffee beans");
                System.out.println("Brewing coffee");
                System.out.println("Adding condiments");


            } else if (n == 2) {
                System.out.println("Making Latte:");
                System.out.println("Grinding coffee beans");
                System.out.println("Brewing coffee");
                System.out.println("Adding milk");
                System.out.println("Adding condiments");

            } else {
                throw new RuntimeException("must be 1 or 2");
            }

            System.out.println();

        }
    }
}

2、问题

  • 虽然是制作不同的咖啡,但制作过程中,有些步骤是一样的。因此,可以复用这些步骤,减少代码冗余。
  • 咖啡是抽象的,但美式咖啡、拿铁是具体的。很容易想到"抽象类-实现子类"这样的代码结构。在抽象类中规范步骤,子类去覆写细节。

三、采用模板方法模式

1、代码

  • 模板
java 复制代码
public interface Coffee {
    CoffeeEnum gotCoffeeEnum();
    void create();
}

public abstract class AbstractCoffee implements Coffee {
    @Override
    public final void create() {
        System.out.printf("Making %s:%n", gotCoffeeEnum().getDesc());
        System.out.println("Grinding coffee beans");
        System.out.println("Brewing coffee");
        addCondiments();
    }

    protected void addCondiments() {
        System.out.println("Adding condiments");
    }
}
  • 实现类
java 复制代码
@Getter
@AllArgsConstructor
public enum CoffeeEnum {
    AMERICAN_COFFEE("American Coffee", 1),
    LATTE("Latte", 2)
    ;

    private final String desc;
    private final int code;

    public static CoffeeEnum getCoffeeEnum(int code) {
        for (CoffeeEnum coffeeEnum : CoffeeEnum.values()) {
            if (coffeeEnum.getCode() == code) {
                return coffeeEnum;
            }
        }
        return null;
    }
}

public class AmericanCoffee extends AbstractCoffee {
    @Override
    public CoffeeEnum gotCoffeeEnum() {
        return CoffeeEnum.AMERICAN_COFFEE;
    }
}


public class LatteCoffee extends AbstractCoffee {
    @Override
    public CoffeeEnum gotCoffeeEnum() {
        return CoffeeEnum.LATTE;
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding milk");
        System.out.println("Adding condiments");
    }
}
  • 外观模式,对各种Coffee的实现类进行封装:
java 复制代码
public class CoffeeFacade {
    private List<Coffee> coffees;

    public CoffeeFacade() {
        this.coffees = new ArrayList<>();
        this.coffees.add(new AmericanCoffee());
        this.coffees.add(new LatteCoffee());
    }

    public void makeCoffee(int code) {
        coffees.stream()
                .filter(coffee -> coffee.gotCoffeeEnum().getCode() == code)
                .findFirst()
                .ifPresent(Coffee::create);
    }
}
  • 客户端:
java 复制代码
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        CoffeeFacade coffeeFacade = new CoffeeFacade();

        while (scanner.hasNextInt()) {
            int n = scanner.nextInt();
            coffeeFacade.makeCoffee(n);
            System.out.println();
        }

    }
}

四、总结

  • 在企业开发中,模板方法模式还是很常见的。通过分析需求,发现类与类的关系呈现下图所示,就可以在抽象类中定义通用步骤(模板),子类去覆写具体细节即可。

  • 当然了,模板方法模式也存在一个明显的弊端:一旦某个实现类不适用当前模板了,不得不去修改抽象类的通用步骤时,便会影响现有实现类(复用的代价)。

  • 比较好的做法是,定义通用步骤时,步骤要足够抽象,而不能太具体太细了,这样可以给子类足够的灵活性。

相关推荐
devmoon1 分钟前
在 Polkadot Runtime 中添加多个 Pallet 实例实战指南
java·开发语言·数据库·web3·区块链·波卡
野犬寒鸦5 分钟前
从零起步学习并发编程 || 第七章:ThreadLocal深层解析及常见问题解决方案
java·服务器·开发语言·jvm·后端·学习
云姜.9 分钟前
java抽象类和接口
java·开发语言
带刺的坐椅9 分钟前
Claude Code Skills,Google A2A Skills,Solon AI Skills 有什么区别?
java·ai·solon·a2a·claudecode·skills
爱学英语的程序员21 分钟前
面试官:你了解过哪些数据库?
java·数据库·spring boot·sql·mysql·mybatis
callJJ1 小时前
Spring AI 文本聊天模型完全指南:ChatModel 与 ChatClient
java·大数据·人工智能·spring·spring ai·聊天模型
CBeann1 小时前
企业级规则引擎落地实战:动态脚本引擎 QLExpress ,真香!
java·ai·大模型·规则引擎·qlexpress·大厂实战项目
懈尘1 小时前
从 Java 1.7 到 Java 21:逐版本深入解析新特性与平台演进
java·开发语言
亓才孓1 小时前
[Maven]Maven基础
java·maven
hello 早上好1 小时前
05_Java 类加载过程
java·开发语言