设计模式-模板方法模式

文章目录

  • 一、概述
    • [1.1 结构与角色](#1.1 结构与角色)
    • [1.2 模板方法中的方法分类](#1.2 模板方法中的方法分类)
    • [1.3 适用场景](#1.3 适用场景)
  • 二、实现方式
    • [2.1 抽象模板](#2.1 抽象模板)
    • [2.2 钩子方法](#2.2 钩子方法)
      • [2.2.1 条件控制型钩子](#2.2.1 条件控制型钩子)
      • [2.2.2 扩展点型钩子](#2.2.2 扩展点型钩子)
  • [三、JDK 源码中的模板方法](#三、JDK 源码中的模板方法)
    • [3.1 AbstractList------列表操作的模板](#3.1 AbstractList——列表操作的模板)
    • [3.2 InputStream------字节输入流的模板](#3.2 InputStream——字节输入流的模板)
    • [3.3 HttpServlet------HTTP 请求处理的模板](#3.3 HttpServlet——HTTP 请求处理的模板)
    • [3.4 Arrays.sort() 中的模板方法思想](#3.4 Arrays.sort() 中的模板方法思想)
  • 四、总结

一、概述

在软件开发中,经常会遇到这样的场景:多个业务流程的整体步骤是固定的,但其中某些步骤的具体实现各不相同。比如:

  • 做饭:买菜 → 洗菜 → 烹饪 → 装盘,其中"烹饪"这一步,炒菜是爆炒、煲汤是慢炖、烤肉是烘烤,步骤相同但实现不同
  • 银行办理业务:取号 → 排队 → 办理业务 → 评价,其中"办理业务"有存款、取款、转账等不同操作
  • 软件开发流程:需求分析 → 设计 → 编码 → 测试 → 部署,其中"编码"可以是 Java、Python、Go 等不同语言实现

如果为每一种具体流程都写一个完整的类,会导致大量重复代码,且当整体步骤发生变化时,所有类都需要修改:
炒菜类
买菜
洗菜
爆炒
装盘
煲汤类
买菜
洗菜
慢炖
装盘
烤肉类
买菜
洗菜
烘烤
装盘

买菜、洗菜、装盘这些步骤在三个类中完全重复,只有"烹饪"这一步不同。

模板方法模式(Template Method Pattern)正是为了解决这个问题而诞生的------它在父类中定义一个算法的骨架(模板方法),将某些步骤推迟到子类中实现,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。

生活中的模板方法例子:

  • 简历模板:简历的框架是固定的(个人信息、教育经历、工作经历、技能特长),但每个人的具体内容不同
  • 开车:发动 → 踩离合 → 挂挡 → 踩油门 → 松离合,手动挡和自动挡的"挂挡"步骤不同,但整体流程一样
  • 考试答题:审题 → 作答 → 检查 → 交卷,不同题型的"作答"方式不同(选择、填空、简答)

核心:在父类中定义算法骨架,将某些步骤延迟到子类中实现,子类可以在不改变算法结构的前提下重新定义某些特定步骤

1.1 结构与角色

模板方法模式包含以下角色:
声明抽象方法
继承
实现抽象方法
Client 客户端
ConcreteClass 具体子类
AbstractClass 抽象模板类

  • AbstractClass(抽象模板类):定义了一组基本方法(抽象方法 + 具体方法),并在模板方法中编排这些基本方法的调用顺序,构成算法骨架
  • ConcreteClass(具体子类):继承抽象模板类,实现父类中声明的抽象方法,也可以覆盖父类的具体方法(钩子方法)
  • Client(客户端):创建具体子类对象,调用模板方法执行完整流程

1.2 模板方法中的方法分类

模板方法模式涉及三类方法:

方法类型 说明 谁来实现
模板方法 定义算法骨架,调用基本方法完成流程 抽象模板类
抽象方法 算法中可变的步骤,必须由子类实现 具体子类
具体方法 算法中固定的步骤,子类可直接继承 抽象模板类
钩子方法 在算法骨架中提供默认实现,子类可选择性覆盖 抽象模板类(默认) / 具体子类(覆盖)

关键点 :模板方法用 final 关键字修饰,防止子类覆盖算法骨架,确保算法结构不被破坏。

1.3 适用场景

  • 多个类有相同的行为,且整体步骤固定,但某些步骤的实现不同
  • 需要统一控制算法的执行流程,同时允许子类自定义部分步骤
  • 需要扩展或定制算法中的某些特定步骤,而不影响整体结构
  • 重构时发现多个子类有公共行为,可以将其提取到父类中

二、实现方式

2.1 抽象模板

以"制作饮品"为例,泡茶和泡咖啡的整体流程相同:烧水 → 冲泡 → 倒入杯中 → 加调料,但冲泡和加调料的步骤不同:
具体子类 Coffee
具体子类 Tea
抽象模板类 CaffeineBeverage
实现
实现
实现
实现
templateMethod: 算法骨架
boilWater: 烧水
brew: 冲泡-抽象方法
pourInCup: 倒入杯中
addCondiments: 加调料-抽象方法
brew: 用热水浸泡茶叶
addCondiments: 加柠檬
brew: 用热水冲泡咖啡粉
addCondiments: 加糖和牛奶

(1)抽象模板类

java 复制代码
/**
 * 抽象模板类:含咖啡因饮品
 * 定义了制作饮品的算法骨架,将可变步骤延迟到子类实现
 */
public abstract class CaffeineBeverage {

    /**
     * 模板方法:定义算法骨架
     * 用 final 修饰,防止子类覆盖算法结构
     */
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    /**
     * 具体方法:烧水(固定步骤,所有子类共用)
     */
    private void boilWater() {
        System.out.println("把水煮沸");
    }

    /**
     * 抽象方法:冲泡(可变步骤,由子类实现)
     */
    protected abstract void brew();

    /**
     * 具体方法:倒入杯中(固定步骤,所有子类共用)
     */
    private void pourInCup() {
        System.out.println("倒入杯中");
    }

    /**
     * 抽象方法:加调料(可变步骤,由子类实现)
     */
    protected abstract void addCondiments();
}

(2)具体子类------茶

java 复制代码
/**
 * 具体子类:茶
 */
public class Tea extends CaffeineBeverage {

    @Override
    protected void brew() {
        System.out.println("用热水浸泡茶叶");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加柠檬");
    }
}

(3)具体子类------咖啡

java 复制代码
/**
 * 具体子类:咖啡
 */
public class Coffee extends CaffeineBeverage {

    @Override
    protected void brew() {
        System.out.println("用热水冲泡咖啡粉");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加糖和牛奶");
    }
}

(4)客户端调用

java 复制代码
public class TemplateMethodDemo {
    public static void main(String[] args) {
        // 制作茶
        CaffeineBeverage tea = new Tea();
        tea.prepareRecipe();
        // 把水煮沸
        // 用热水浸泡茶叶
        // 倒入杯中
        // 加柠檬

        System.out.println("---");

        // 制作咖啡
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
        // 把水煮沸
        // 用热水冲泡咖啡粉
        // 倒入杯中
        // 加糖和牛奶
    }
}

关键点 :客户端只调用 prepareRecipe() 模板方法,算法的执行流程由父类统一控制。新增一种饮品只需新增一个子类并实现两个抽象方法,无需修改父类和已有的子类,符合开闭原则

2.2 钩子方法

钩子方法(Hook Method)是模板方法模式中的一种特殊方法------它在抽象模板类中提供了默认实现 (通常是空实现),子类可以选择性覆盖,用于控制算法流程或扩展功能。

钩子方法有两种常见用法:

用法 说明 示例
条件控制 在模板方法中通过钩子方法的返回值决定是否执行某个步骤 customerWantsCondiments() 控制是否加调料
扩展点 提供一个空实现的钩子方法,子类按需覆盖以插入自定义逻辑 beforeBrew() / afterBrew() 前后置钩子

2.2.1 条件控制型钩子

以"制作饮品"为例,有些人喝茶不加任何调料,我们可以通过钩子方法来控制是否执行"加调料"这一步:

java 复制代码
/**
 * 抽象模板类:含咖啡因饮品(带钩子方法)
 */
public abstract class CaffeineBeverageWithHook {

    /**
     * 模板方法:定义算法骨架
     * 通过钩子方法控制"加调料"步骤是否执行
     */
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    private void boilWater() {
        System.out.println("把水煮沸");
    }

    protected abstract void brew();

    private void pourInCup() {
        System.out.println("倒入杯中");
    }

    protected abstract void addCondiments();

    /**
     * 钩子方法:客户是否需要加调料
     * 默认返回 true,子类可以覆盖此方法来改变行为
     *
     * @return true 表示需要加调料,false 表示不加
     */
    protected boolean customerWantsCondiments() {
        return true;
    }
}

(2)具体子类------茶(不加调料)

java 复制代码
/**
 * 具体子类:茶(不加调料)
 * 覆盖钩子方法,返回 false,跳过"加调料"步骤
 */
public class TeaWithHook extends CaffeineBeverageWithHook {

    @Override
    protected void brew() {
        System.out.println("用热水浸泡茶叶");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加柠檬");
    }

    /**
     * 覆盖钩子方法:茶不加调料
     */
    @Override
    protected boolean customerWantsCondiments() {
        return false;
    }
}

(3)具体子类------咖啡(加调料)

java 复制代码
/**
 * 具体子类:咖啡(加调料)
 * 不覆盖钩子方法,使用默认值 true
 */
public class CoffeeWithHook extends CaffeineBeverageWithHook {

    @Override
    protected void brew() {
        System.out.println("用热水冲泡咖啡粉");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加糖和牛奶");
    }
}

(4)客户端调用

java 复制代码
public class HookDemo {
    public static void main(String[] args) {
        // 制作茶(不加调料)
        CaffeineBeverageWithHook tea = new TeaWithHook();
        tea.prepareRecipe();
        // 把水煮沸
        // 用热水浸泡茶叶
        // 倒入杯中
        // (没有加调料,因为 customerWantsCondiments() 返回 false)

        System.out.println("---");

        // 制作咖啡(加调料)
        CaffeineBeverageWithHook coffee = new CoffeeWithHook();
        coffee.prepareRecipe();
        // 把水煮沸
        // 用热水冲泡咖啡粉
        // 倒入杯中
        // 加糖和牛奶
    }
}

关键点 :钩子方法 customerWantsCondiments() 提供了默认实现(返回 true),子类可以按需覆盖。模板方法中通过 if (customerWantsCondiments()) 判断是否执行"加调料"步骤,实现了灵活的流程控制

2.2.2 扩展点型钩子

钩子方法还可以作为扩展点,让子类在不改变算法骨架的前提下插入自定义逻辑。这种用法类似于 Spring 框架中的 InitializingBean.afterPropertiesSet()@PostConstruct

以"数据库操作模板"为例,提供 beforeExecute()afterExecute() 两个钩子方法,让子类可以在执行前后插入自定义逻辑(如日志、监控、事务等):

java 复制代码
/**
 * 抽象模板类:数据库操作模板
 * 提供前后置钩子方法,子类可按需扩展
 */
public abstract class DatabaseOperationTemplate {

    /**
     * 模板方法:定义数据库操作的算法骨架
     */
    public final void execute() {
        beforeExecute();
        openConnection();
        doOperation();
        closeConnection();
        afterExecute();
    }

    private void openConnection() {
        System.out.println("打开数据库连接");
    }

    /**
     * 抽象方法:具体的数据库操作
     */
    protected abstract void doOperation();

    private void closeConnection() {
        System.out.println("关闭数据库连接");
    }

    /**
     * 钩子方法:操作前扩展点(默认空实现)
     * 子类可覆盖,如记录开始时间、开启事务等
     */
    protected void beforeExecute() {
        // 默认空实现
    }

    /**
     * 钩子方法:操作后扩展点(默认空实现)
     * 子类可覆盖,如记录结束时间、提交事务等
     */
    protected void afterExecute() {
        // 默认空实现
    }
}

(2)具体子类------带日志的查询操作

java 复制代码
/**
 * 具体子类:带日志的查询操作
 * 利用钩子方法在操作前后记录耗时
 */
public class QueryWithLog extends DatabaseOperationTemplate {

    private long startTime;

    @Override
    protected void doOperation() {
        System.out.println("执行 SELECT 查询");
    }

    @Override
    protected void beforeExecute() {
        startTime = System.currentTimeMillis();
        System.out.println("[LOG] 查询开始");
    }

    @Override
    protected void afterExecute() {
        long cost = System.currentTimeMillis() - startTime;
        System.out.println("[LOG] 查询完成,耗时:" + cost + "ms");
    }
}

(3)具体子类------简单的更新操作

java 复制代码
/**
 * 具体子类:简单的更新操作
 * 不覆盖钩子方法,只实现核心操作
 */
public class SimpleUpdate extends DatabaseOperationTemplate {

    @Override
    protected void doOperation() {
        System.out.println("执行 UPDATE 更新");
    }
}

(4)客户端调用

java 复制代码
public class DatabaseDemo {
    public static void main(String[] args) {
        // 带日志的查询
        DatabaseOperationTemplate query = new QueryWithLog();
        query.execute();
        // [LOG] 查询开始
        // 打开数据库连接
        // 执行 SELECT 查询
        // 关闭数据库连接
        // [LOG] 查询完成,耗时:0ms

        System.out.println("---");

        // 简单更新(无日志)
        DatabaseOperationTemplate update = new SimpleUpdate();
        update.execute();
        // 打开数据库连接
        // 执行 UPDATE 更新
        // 关闭数据库连接
    }
}

关键点 :扩展点型钩子方法提供空实现,子类按需覆盖。查询操作利用钩子记录日志,更新操作不覆盖钩子、保持简洁。这种设计让算法骨架保持稳定,同时提供了足够的扩展性。


三、JDK 源码中的模板方法

模板方法模式在 JDK 中有着广泛的应用,下面来看几个经典的例子。

3.1 AbstractList------列表操作的模板

java.util.AbstractList 是模板方法模式的典型代表。它实现了 List<E> 接口,在 get(int index)set(int index, E element)add(int index, E element)remove(int index) 等方法中提供了基于随机访问的默认实现,而将 get(int index)size() 声明为抽象方法,由子类实现:

java 复制代码
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {

    /**
     * 抽象方法:获取指定位置的元素(子类必须实现)
     */
    public abstract E get(int index);

    /**
     * 抽象方法:返回列表大小(子类必须实现)
     */
    public abstract int size();

    /**
     * 模板方法中的可覆盖步骤:设置指定位置的元素
     * 默认抛出 UnsupportedOperationException,子类可按需覆盖
     */
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }

    /**
     * 模板方法中的可覆盖步骤:在指定位置插入元素
     * 默认抛出 UnsupportedOperationException,子类可按需覆盖
     */
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

    /**
     * 模板方法中的可覆盖步骤:移除指定位置的元素
     * 默认抛出 UnsupportedOperationException,子类可按需覆盖
     */
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
}

JDK 中的继承体系:
List 接口
AbstractList 抽象模板类
ArrayList 具体子类
Vector 具体子类
AbstractSequentialList 中间抽象类
LinkedList 具体子类

在这个例子中:

  • AbstractClass(抽象模板类)AbstractList,定义了 get()size() 为抽象方法,set()add()remove() 为可覆盖步骤(抛出异常作为默认实现,类似于钩子方法)
  • ConcreteClass(具体子类)ArrayList 实现了基于数组的 get()LinkedList 通过 AbstractSequentialList 实现了基于链表的 get()

关键点AbstractListset()add()remove() 的默认实现抛出 UnsupportedOperationException,这就是一种钩子方法的变体------默认行为是"不支持",子类按需覆盖以提供实际功能。

3.2 InputStream------字节输入流的模板

java.io.InputStream 是另一个经典案例。它定义了 read() 方法的骨架,子类只需实现读取单个字节的 read() 方法:

java 复制代码
public abstract class InputStream implements Closeable {

    /**
     * 抽象方法:读取数据的下一个字节(子类必须实现)
     */
    public abstract int read() throws IOException;

    /**
     * 模板方法:读取多个字节到字节数组中
     * 内部循环调用抽象方法 read() 逐个读取
     */
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();  // 调用抽象方法
        if (c == -1) {
            return -1;
        }
        b[off] = (byte) c;

        int i = 1;
        for (; i < len; i++) {
            c = read();  // 调用抽象方法
            if (c == -1) {
                break;
            }
            b[off + i] = (byte) c;
        }
        return i;
    }

    /**
     * 钩子方法:可跳过的字节数
     * 默认返回 0,子类可覆盖
     */
    public long skip(long n) throws IOException {
        // 默认实现...
        return 0;
    }
}

JDK 中的继承体系:
InputStream 抽象模板类
FileInputStream
ByteArrayInputStream
BufferedInputStream
ObjectInputStream

在这个例子中:

  • AbstractClass(抽象模板类)InputStream,定义了 read() 为抽象方法
  • ConcreteClass(具体子类)FileInputStream(从文件读取)、ByteArrayInputStream(从字节数组读取)等,各自实现 read() 方法

关键点InputStream.read(byte[], int, int) 就是模板方法,它编排了算法流程------循环调用抽象方法 read() 逐字节读取,子类只需关心如何读取单个字节,不用操心缓冲区、边界检查等通用逻辑。

3.3 HttpServlet------HTTP 请求处理的模板

在 Java Web 开发中,javax.servlet.http.HttpServlet 也是模板方法模式的经典应用。它的 service() 方法根据 HTTP 请求方法分发给对应的 doXxx() 方法:

java 复制代码
public abstract class HttpServlet extends GenericServlet {

    /**
     * 模板方法:根据请求方法分发到对应的 doXxx 方法
     */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String method = req.getMethod();

        if ("GET".equals(method)) {
            doGet(req, resp);
        } else if ("POST".equals(method)) {
            doPost(req, resp);
        } else if ("PUT".equals(method)) {
            doPut(req, resp);
        } else if ("DELETE".equals(method)) {
            doDelete(req, resp);
        } else if ("HEAD".equals(method)) {
            doHead(req, resp);
        } else if ("OPTIONS".equals(method)) {
            doOptions(req, resp);
        } else if ("TRACE".equals(method)) {
            doTrace(req, resp);
        } else {
            // 不支持的请求方法
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
        }
    }

    /**
     * 钩子方法:处理 GET 请求
     * 默认返回 405 错误,子类按需覆盖
     */
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
    }

    /**
     * 钩子方法:处理 POST 请求
     * 默认返回 405 错误,子类按需覆盖
     */
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
    }

    // doPut、doDelete、doHead、doOptions、doTrace 类似...
}

使用方式:

java 复制代码
/**
 * 自定义 Servlet:覆盖 doGet 和 doPost 钩子方法
 */
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("<h1>处理 GET 请求</h1>");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String name = req.getParameter("name");
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("<h1>处理 POST 请求,name=" + name + "</h1>");
    }
}

在这个例子中:

  • AbstractClass(抽象模板类)HttpServletservice() 是模板方法,doGet()doPost() 等是钩子方法
  • ConcreteClass(具体子类) :自定义 Servlet,覆盖需要的 doXxx() 钩子方法
  • 钩子方法的特点:默认返回 405 错误,子类只需覆盖自己支持的 HTTP 方法,不需要的保持默认即可

关键点HttpServlet.service() 是模板方法,它定义了 HTTP 请求分发的算法骨架。doGet()doPost() 等是钩子方法,提供默认实现(返回 405),子类按需覆盖,这就是条件控制型钩子在真实框架中的应用。

3.4 Arrays.sort() 中的模板方法思想

java.util.Arrays 中的排序方法也体现了模板方法的思想------排序算法的骨架是固定的(基于 TimSort / 双轴快排),而元素之间的比较规则由外部传入的 Comparator 决定:

java 复制代码
import java.util.Arrays;
import java.util.Comparator;

public class ArraysSortTemplateDemo {
    public static void main(String[] args) {
        String[] names = {"Charlie", "Alice", "Bob", "David"};

        // 算法骨架固定,比较策略由 Comparator 决定
        Arrays.sort(names, Comparator.comparingInt(String::length));
        System.out.println("按长度排序:" + Arrays.toString(names));
        // 按长度排序:[Bob, Alice, David, Charlie]

        Arrays.sort(names, Comparator.naturalOrder());
        System.out.println("按字典序排序:" + Arrays.toString(names));
        // 按字典序排序:[Alice, Bob, Charlie, David]
    }
}

说明 :严格来说,Arrays.sort() 结合 Comparator策略模式 + 模板方法 的混合应用------排序算法的骨架是模板方法,比较策略由 Comparator 策略接口决定。


四、总结

模板方法模式的核心思想是在父类中定义算法骨架,将可变步骤延迟到子类中实现,子类可以在不改变算法结构的前提下重新定义某些特定步骤。

优点:

  • 代码复用:公共步骤在父类中实现一次,所有子类共享,避免重复代码
  • 符合开闭原则:新增具体子类只需实现抽象方法,无需修改父类和已有子类
  • 统一控制算法流程:模板方法用 final 修饰,确保算法结构不被破坏
  • 灵活扩展:钩子方法提供了灵活的扩展点,子类可选择性覆盖
  • 反向控制:父类调用子类的方法(而非子类调用父类),实现了"好莱坞原则"------Don't call us, we'll call you

缺点:

  • 类数量增加:每种具体实现都需要一个子类,实现越多子类越多
  • 子类受限于父类:算法骨架由父类定义,子类只能扩展而不能改变算法结构
  • 可读性下降:算法分散在父类和多个子类中,理解完整流程需要跳转多个类

适用场景:

  • 多个类有相同的行为,整体步骤固定,但某些步骤的实现不同
  • 需要统一控制算法执行流程,同时允许子类自定义部分步骤
  • 需要在算法的某些步骤前后插入自定义逻辑(钩子方法)

模板方法模式 vs 策略模式 :模板方法模式通过继承 实现算法骨架的复用,子类覆盖部分步骤来改变行为;策略模式通过组合 实现算法的灵活切换,上下文持有策略接口的引用。模板方法模式侧重于固定算法结构 、变化部分步骤;策略模式侧重于整体算法替换、运行时动态切换。

对比维度 模板方法模式 策略模式
实现方式 继承 组合
算法控制 父类固定算法骨架 策略可整体替换
扩展方式 新增子类 新增策略类
运行时切换 不支持(编译时确定子类) 支持(运行时切换策略对象)
代码复用 父类公共代码被所有子类共享 策略间无代码共享
设计原则 好莱坞原则 开闭原则 + 合成复用原则

参考博客:

模板方法模式 | 菜鸟教程:https://www.runoob.com/design-pattern/template-pattern.html

相关推荐
Autumn_ing5 小时前
2026实测:这5款AI生成UI工具支持Shadcn UI/Ant Design组件库
人工智能·ui·设计模式·aigc·设计规范
woniu_buhui_fei10 小时前
常用设计模式
设计模式·架构
likerhood10 小时前
设计模式 · 组合模式(Composite Pattern)
设计模式·组合模式
多加点辣也没关系10 小时前
设计模式-迭代器模式
设计模式·迭代器模式
江米小枣tonylua1 天前
从红绿灯到方向盘:TDD 在 AI 时代的新角色
前端·设计模式·ai编程
nnsix1 天前
设计模式 - 工厂模式 笔记
笔记·设计模式
洛水水1 天前
结构性设计模式详解
c++·设计模式
多加点辣也没关系1 天前
设计模式-策略模式
java·设计模式·策略模式