对模板方法模式的理解

对模板方法模式的理解

一、场景

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();
        }

    }
}

四、总结

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

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

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

相关推荐
c++之路42 分钟前
C++20概述
java·开发语言·c++20
Championship.23.241 小时前
Linux Top 命令族深度解析与实战指南
java·linux·服务器·top·linux调试
橘子海全栈攻城狮1 小时前
【最新源码】养老院系统管理A013
java·spring boot·后端·web安全·微信小程序
逻辑驱动的ken1 小时前
Java高频面试考点18
java·开发语言·数据库·算法·面试·职场和发展·哈希算法
冷雨夜中漫步2 小时前
Claude Code源码分析——Claude Code Agent Loop 详细设计文档
java·开发语言·人工智能·ai
直奔標竿2 小时前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring
one_love_zfl2 小时前
java面试-微服务组件篇
java·微服务·面试
一只大袋鼠2 小时前
Java进阶:CGLIB动态代理解析
java·开发语言
环流_3 小时前
HTTP 协议的基本格式
java·网络协议·http
爱滑雪的码农3 小时前
Java基础十三:Java中的继承、重写(Override)与重载(Overload)详解
java·开发语言