面试必看:Java 设计模式面试高频题总结:工厂、策略、责任链的答案与思路

Java 设计模式面试高频题总结:工厂、策略、责任链的答案与思路

设计模式是 Java 面试中考察 “代码设计能力” 的核心考点,面试官不仅关注你是否 “知道模式”,更看重你是否能 “理解原理、结合场景、灵活应用”。本文聚焦工厂模式、策略模式、责任链模式三大高频实用模式,整理了面试中最常问的 20 + 问题,每个问题均提供 “核心思路 + 标准答案”,帮你快速掌握面试要点。

一、工厂模式高频题

工厂模式是创建型模式的核心,面试中常围绕 “三种工厂的区别、适用场景、实战选型” 展开提问。

1. 什么是简单工厂模式?它有什么优缺点?

核心思路:定义 + 角色 + 优缺点,结合 “违反开闭原则” 的关键缺陷。

参考答案

简单工厂模式是工厂模式的入门形态,通过一个 “统一工厂类” 创建所有产品,核心角色包括:

  • 抽象产品:定义产品规范(如Coffee);
  • 具体产品:实现抽象产品(如AmericanCoffee);
  • 具体工厂:提供静态方法,根据参数(如 “american”“latte”)创建对应产品。

优点

  • 解耦对象创建与使用(客户端无需直接new对象);
  • 统一管理产品创建逻辑(如所有咖啡统一加牛奶和糖)。

缺点

  • 违反 “开闭原则”:新增产品需修改工厂的if-else分支(如加 “摩卡咖啡” 需改createCoffee方法);
  • 产品过多时工厂类会臃肿,维护成本高。

适用场景:产品类型少、变化不频繁的简单场景(如工具类创建、小型项目)。

2. 工厂方法模式和简单工厂模式的区别是什么?为什么工厂方法更符合开闭原则?

核心思路:对比 “工厂数量、开闭原则支持、复杂度”,重点讲工厂方法的 “一个产品一个工厂” 设计。

参考答案

两者核心区别在于 “工厂与产品的对应关系” 和 “对开闭原则的支持”:

对比维度简单工厂模式工厂方法模式
工厂数量一个万能工厂创建所有产品一个产品对应一个具体工厂
开闭原则支持不支持(新增产品改工厂)支持(新增产品加工厂,不改原有代码)
复杂度简单(1 个工厂类)中等(N 个产品对应 N 个工厂类)
客户端依赖依赖具体工厂 + 抽象产品依赖抽象工厂 + 抽象产品(更解耦)

工厂方法符合开闭原则的原因

新增产品时,只需新增 “具体产品类”(如MochaCoffee)和 “对应具体工厂类”(如MochaCoffeeFactory),无需修改原有工厂或产品代码,完全符合 “扩展开放、修改关闭” 的原则。

示例:新增摩卡咖啡时,简单工厂需改if-else,工厂方法只需加MochaCoffeeMochaCoffeeFactory,原有代码不变。

3. 什么是抽象工厂模式?它和工厂方法模式的核心区别是什么?

核心思路:先解释 “产品族” 概念,再对比 “产品维度、适用场景”。

参考答案

抽象工厂模式是工厂方法的 “升级版”,用于创建多个维度的产品族(如 “美式风味” 包含 “美式咖啡 + 抹茶慕斯”,“意大利风味” 包含 “拿铁咖啡 + 提拉米苏”),核心角色包括:

  • 抽象产品(多个,如CoffeeDessert);
  • 具体产品(如AmericanCoffeeMatchaMousse);
  • 抽象工厂:定义多产品创建接口(如createCoffee()+createDessert());
  • 具体工厂:创建对应产品族(如AmericanFlavorFactory创建美式咖啡 + 抹茶慕斯)。

与工厂方法的核心区别

  • 产品维度:工厂方法只处理 “单维度产品”(如仅咖啡),抽象工厂处理 “多维度产品族”(如咖啡 + 甜点);
  • 适用场景:工厂方法适合 “单一产品类型”,抽象工厂适合 “多产品需配套创建”(如同一品牌下的多种产品)。

示例:咖啡店扩展甜点业务时,工厂方法需为 “咖啡” 和 “甜点” 分别建工厂,抽象工厂可通过 “风味工厂” 一次性创建配套的咖啡和甜点,确保产品族一致性。

4. 实际项目中如何选择三种工厂模式?举个你项目中的例子。

核心思路:按 “产品数量、变化频率、维度” 分场景选型,结合真实项目案例。

参考答案

选型的核心是 “匹配业务复杂度”,具体原则如下:

业务场景推荐模式项目案例
产品少(≤5 种)、变化少简单工厂工具类创建(如DateUtilscreateDate方法)
产品多、变化频繁、单维度工厂方法支付方式创建(支付宝、微信、银行卡对应 3 个工厂)
产品多、多维度(产品族)抽象工厂电商平台的 “品牌产品族”(华为手机 + 华为耳机)

项目案例

我在电商项目中负责 “订单支付模块”,支付方式包括 “支付宝、微信、银行卡”,且后续可能加 “银联”“PayPal”。

  • 初期考虑过简单工厂,但新增支付方式需改if-else,不符合开闭原则;

  • 最终选

    工厂方法模式:

    • 抽象产品:Payment(定义pay方法);
    • 具体产品:AlipayPaymentWechatPayment
    • 具体工厂:AlipayFactoryWechatFactory
    • 新增 “银联支付” 时,只需加UnionPayPaymentUnionPayFactory,原有代码零修改,符合项目 “支付方式频繁扩展” 的需求。

二、策略模式高频题

策略模式是行为型模式的 “去 if-else 利器”,面试中常围绕 “模式原理、实战优化、与工厂的结合” 提问。

1. 什么是策略模式?它能解决什么问题?

核心思路:定义 + 角色 + 核心价值,重点讲 “消除 if-else” 的关键作用。

参考答案

策略模式定义 “一系列算法 / 行为”,将每个算法封装为独立策略类,使算法可动态切换,核心角色包括:

  • 抽象策略:定义算法接口(如TravelStrategytravel方法);
  • 具体策略:实现抽象策略(如BicycleStrategyCarStrategy);
  • 环境类:持有策略引用,为客户端提供统一调用入口(如TravelContext)。

核心解决的问题

  • 消除代码中冗长的if-elseswitch分支(如 “根据出行方式选择逻辑”“根据登录类型选择验证逻辑”);
  • 实现算法的 “动态切换”(如用户可手动切换出行方式、系统可根据场景自动切换优惠规则);
  • 解耦 “算法定义” 与 “算法使用”(客户端无需知道算法细节,只需调用环境类)。

反例:没有策略模式时,登录逻辑可能写成:

// 冗余if-else,新增登录方式需改代码
public User login(String type, String username, String credential) {
    if ("account".equals(type)) {
        // 用户名密码登录
    } else if ("sms".equals(type)) {
        // 短信登录
    } else if ("wechat".equals(type)) {
        // 微信登录
    }
    // ...
}

2. 策略模式和工厂模式结合使用的场景是什么?举个实战案例。

核心思路:说明 “策略解决行为切换,工厂解决策略创建” 的互补性,结合 Spring 环境的实战案例。

参考答案

纯策略模式需客户端手动创建策略对象(如context.setStrategy(new CarStrategy())),结合工厂模式可实现 “根据参数自动选择策略”,进一步降低客户端复杂度,两者结合是企业开发的高频组合。

适用场景:需 “根据参数动态选择策略” 的场景(如根据前端传入的grantType选择登录策略、根据订单金额选择优惠策略)。

实战案例(多方式登录)

在 Spring Boot 项目中实现 “用户名密码、短信、微信” 三种登录方式,采用 “策略 + 工厂” 组合:

  1. 策略层:
    • 抽象策略UserGranter:定义grantType()(返回登录类型)和login()(登录逻辑);
    • 具体策略AccountGranter(账号登录)、SmsGranter(短信登录)、WechatGranter(微信登录),用@Component注入 Spring 容器。
  2. 工厂层:
    • 工厂UserGranterFactory通过List<UserGranter>自动收集所有策略,用Map存储 “类型→策略” 映射;
    • 提供getGranter(String grantType)方法,根据类型返回对应策略。
  3. 使用层:
    • 登录服务LoginService注入工厂,接收前端grantType参数,通过工厂获取策略并执行登录。

核心优势

  • 客户端无需知道策略类,只需传递参数;
  • 新增登录方式只需加XXXGranter类,无需修改工厂或服务代码,完全符合开闭原则。

3. 策略模式的优缺点是什么?实际项目中如何避免 “策略类过多” 的问题?

核心思路:优缺点围绕 “扩展性、复杂度”,解决策略过多需结合 “工厂 + 配置 + 动态加载”。

参考答案

优点

  • 扩展性强:新增策略只需加类,无需改原有代码;
  • 代码清晰:消除if-else,每个策略职责单一;
  • 动态切换:支持运行时切换策略(如用户手动选择支付方式)。

缺点

  • 策略类过多:每个算法对应一个类,产品 / 算法多时会产生大量类(如 10 种优惠规则对应 10 个策略类);
  • 客户端需了解策略:客户端需知道策略类型(如 “account”“sms”),才能选择对应策略。

解决 “策略类过多” 的方案

  1. 结合配置文件:将简单策略的逻辑配置化(如优惠规则 “满 300 减 50” 写在配置文件,无需建类);
  2. 策略组合:将多个小策略组合为大策略(如 “满减 + 优惠券” 组合策略,复用已有小策略);
  3. 动态加载:用 Spring 的@Conditional注解,根据配置动态加载策略(如仅加载启用的登录方式);
  4. 使用枚举策略:简单场景用枚举实现策略(如支付方式枚举,每个枚举值对应登录逻辑),减少类数量。

示例:简单优惠规则可用配置文件定义:

discount:
  rules:
    - type: full300Minus50
      minAmount: 300
      minus: 50
    - type: full500Minus100
      minAmount: 500
      minus: 100

工厂读取配置生成策略,无需为每个规则建类。

三、责任链模式高频题

责任链模式是行为型模式中 “处理流程解耦” 的核心,面试中常围绕 “链的构建、场景应用、与过滤器的关系” 提问。

1. 什么是责任链模式?它的核心角色有哪些?

核心思路:定义 + 角色 + 流程,结合 “请求链式传递” 的核心逻辑。

参考答案

责任链模式将请求的处理者连成一条链,请求沿着链传递,直到有处理者能处理它(或链结束),核心目的是 “解耦请求发送者与多个处理者”,避免发送者直接依赖所有处理者。

核心角色

  • 抽象处理者(Handler):定义处理请求的接口,包含 “处理方法”(如handle)和 “设置下一个处理者” 的方法(如setNextHandler);
  • 具体处理者(ConcreteHandler):实现抽象处理者,判断是否能处理请求(或直接处理后传递),如订单流程的 “参数检验 handler”“价格计算 handler”;
  • 客户端(Client):创建责任链,发起请求(如OrderService构建链并调用handle)。

核心流程

客户端发起请求→链头处理者接收→处理者处理(或直接传递)→下一个处理者→…→链尾处理者(或请求被处理)。

示例:订单创建流程的链:ParamCheckHandlerDataFillHandlerPriceCalculateHandlerOrderSaveHandler,请求依次传递,每个 handler 完成自己的职责后传递给下一个。

2. 责任链模式和过滤器模式(如 SpringMVC Filter)是什么关系?

核心思路:说明 “过滤器模式是责任链的特殊形态”,结合 SpringMVC Filter 的实战案例。

参考答案

过滤器模式(Filter Pattern)是责任链模式的 “特殊应用”,两者核心逻辑一致(请求链式传递、处理者各司其职),区别在于 “处理者的职责定位”:

  • 责任链模式:处理者通常 “完成一个完整的业务步骤”(如订单的参数检验、价格计算),请求必须被处理(或抛出异常);
  • 过滤器模式:处理者通常 “对请求 / 响应进行过滤或增强”(如日志打印、权限校验、字符编码转换),过滤器可选择 “拦截请求” 或 “放行请求”。

SpringMVC Filter 的责任链实现

SpringMVC 的Filter就是典型的责任链应用:

  1. 每个Filter是 “具体处理者”,实现doFilter(ServletRequest, ServletResponse, FilterChain)方法;
  2. FilterChain是 “责任链管理器”,维护过滤器列表,调用chain.doFilter()将请求传递给下一个过滤器;
  3. 客户端(浏览器)发起请求→Filter1(日志)→Filter2(权限)→Filter3(编码)→Servlet,每个过滤器完成自己的职责后放行。

结论:过滤器模式是责任链模式在 “请求过滤场景” 的具体化,本质上属于责任链模式的范畴。

3. 如何实现 “动态调整责任链的顺序”?举个项目中的例子。

核心思路:结合配置文件或数据库存储链顺序,工厂动态构建链,避免硬编码顺序。

参考答案

硬编码责任链(如paramCheck.setNextHandler(dataFill).setNextHandler(priceCalculate))无法动态调整顺序,实际项目中可通过 “配置化” 实现动态调整,核心步骤:

  1. 定义处理者标识:每个具体处理者定义唯一标识(如paramCheckdataFill”);
  2. 配置链顺序:在配置文件(如 yaml)或数据库中定义链的顺序(如orderHandlers: [paramCheck, dataFill, couponDeduct, priceCalculate, orderSave]);
  3. 工厂动态构建链:工厂从 Spring 容器中获取所有处理者,根据配置顺序组装链。

项目案例(订单流程动态调整)

在电商项目中,不同类型的订单(普通订单、秒杀订单)需走不同的流程:

  • 普通订单流程:参数检验→数据填充→优惠券抵扣→价格计算→保存;
  • 秒杀订单流程:参数检验→库存锁定→数据填充→价格计算→保存;

通过配置文件动态定义流程:

order:
  flow:
    normal: [paramCheck, dataFill, couponDeduct, priceCalculate, orderSave]
    seckill: [paramCheck, stockLock, dataFill, priceCalculate, orderSave]

工厂构建链的代码

@Component
public class OrderHandlerFactory {
    // 从Spring容器收集所有处理者,key=标识,value=handler
    private final Map<String, OrderHandler> handlerMap;

    public OrderHandlerFactory(List<OrderHandler> handlers) {
        this.handlerMap = handlers.stream()
                .collect(Collectors.toMap(OrderHandler::getHandlerId, handler -> handler));
    }

    // 根据订单类型构建链
    public OrderHandler buildChain(String orderType) {
        // 从配置文件获取该类型的链顺序
        List<String> handlerIds = getHandlerIdsFromConfig(orderType);
        if (handlerIds.isEmpty()) {
            throw new RuntimeException("无对应订单流程配置");
        }

        // 组装链
        OrderHandler head = null;
        OrderHandler current = null;
        for (String handlerId : handlerIds) {
            OrderHandler handler = handlerMap.get(handlerId);
            if (handler == null) {
                throw new RuntimeException("处理者不存在:" + handlerId);
            }
            if (head == null) {
                head = handler;
                current = handler;
            } else {
                current = current.setNextHandler(handler);
            }
        }
        return head;
    }

    // 从配置文件获取链顺序(省略实现)
    private List<String> getHandlerIdsFromConfig(String orderType) { /* ... */ }
}

核心优势:新增订单类型或调整流程时,只需修改配置,无需改代码,灵活性极强。

4. 责任链模式的优缺点是什么?实际项目中如何避免 “请求未被处理” 的问题?

核心思路:优缺点围绕 “解耦、灵活性、性能”,未处理问题需 “默认处理、异常兜底、链校验”。

参考答案

优点

  • 解耦请求发送者与处理者(发送者无需知道谁处理、处理顺序);
  • 灵活调整流程:可动态增删处理者、调整顺序;
  • 单一职责:每个处理者只负责自己的职责(如参数检验只做校验)。

缺点

  • 请求可能未被处理:若链中所有处理者都不处理请求,会导致请求 “无响应”;
  • 链过长影响性能:处理者过多时,请求传递会增加开销;
  • 链的合理性依赖客户端:客户端需正确构建链,否则会出现 “循环调用” 或 “处理顺序错误”。

避免 “请求未被处理” 的方案

  1. 添加默认处理者:在链尾添加 “默认处理者”,若前面所有处理者都不处理,默认处理(如返回 “请求无法处理” 的提示);
  2. 异常兜底:在环境类中捕获 “未处理” 的情况,抛出明确异常(如throw new RuntimeException("请求未被任何处理者处理"));
  3. 链构建校验:工厂构建链时,校验每个处理者是否能 “处理或传递”,避免出现 “断链”;
  4. 明确处理规则:设计处理者时约定 “必须处理或传递”,不允许 “既不处理也不传递”(如订单流程的每个步骤都必须执行,不存在 “不处理” 的情况)。

四、面试总结

设计模式面试的核心是 “原理理解 + 场景结合 + 实战思考”,单纯背定义很难通过深度追问,需重点掌握以下几点:

1. 三大模式的核心定位

  • 工厂模式:解决 “对象创建” 的问题,核心是 “解耦创建与使用”,三种工厂的选型取决于 “产品数量、变化频率、维度”;
  • 策略模式:解决 “算法 / 行为切换” 的问题,核心是 “消除 if-else”,实战中常与工厂结合实现 “动态选策略”;
  • 责任链模式:解决 “请求链式处理” 的问题,核心是 “解耦流程与处理者”,适合订单、审批、过滤等流程化场景。

2. 面试答题的关键技巧

  • 结合场景:每个模式的回答都要带 “项目案例”(如工厂模式讲 “支付方式创建”,策略模式讲 “多方式登录”),避免空泛;
  • 对比差异:明确区分相似模式(如工厂方法 vs 抽象工厂、责任链 vs 过滤器),体现理解深度;
  • 承认局限:不回避模式的缺点(如策略类过多、责任链性能问题),并给出解决方案(如配置化、动态加载),体现实战能力。

3. 高频追问的应对思路

  • 当被问 “为什么用这个模式” 时,要讲 “不用模式的痛点”(如不用策略会有 if-else,不用工厂会耦合创建);
  • 当被问 “如何优化模式的缺点” 时,要结合框架特性(如 Spring 的自动装配、配置文件动态调整);
  • 当被问 “项目中遇到的问题” 时,要讲 “模式落地的坑”(如工厂方法的类爆炸、责任链的循环调用)及解决方案。

掌握这三大模式,不仅能应对面试,更能在实际项目中写出 “高内聚、低耦合、易扩展” 的代码,这也是从 “初级开发者” 向 “中高级开发者” 进阶的关键一步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值