1. 观察者模式?发布订阅的区别?

观察者模式:主题状态变化时自动通知所有订阅者。解耦"通知者"与"被通知者"。

应用:Spring 事件机制(ApplicationEvent + @EventListener)、MQ 的发布订阅模型(广义)、Swing/AWT 事件监听。

观察者 vs 发布-订阅:观察者模式中主题与观察者直接关联(主题持有观察者列表);发布-订阅中间有消息代理(Topic),完全解耦。Spring 事件本质是观察者(ApplicationEventPublisher 直接持有监听器列表,没有独立的 broker 进程);只有 MQ 这种经过消息代理中转的才是发布-订阅。

Spring 事件驱动(发布-订阅形态)
// 事件
class OrderCreatedEvent extends ApplicationEvent {
    OrderCreatedEvent(Object source) { super(source); }
}

// 发布(业务代码解耦)
@Service class OrderService {
    @Autowired ApplicationEventPublisher publisher;
    public void create() {
        // 订单逻辑...
        publisher.publishEvent(new OrderCreatedEvent(this));  // 通知,不关心谁听
    }
}

// 监听(可多个,互不感知)
@Component class SmsListener {
    @EventListener
    public void onOrder(OrderCreatedEvent e) { // 发短信 }
}

🎯 面试要点

  • Spring 事件默认同步执行(同一线程),@Async + @EnableAsync 可异步——面试延伸点
  • 观察者模式的本质:一对多的依赖,变化自动传播

2. 策略模式?和 if-else 的关系?

策略模式:定义一族算法,各自封装,运行时可互换。客户端依赖策略接口,不依赖具体算法。

if-else 消除:多个 if-else 分支各对应一个策略类 + 注册表(Map<类型, 策略>,构造时注册)。

策略模式 + 注册表(改造 if-else)
interface PayStrategy { void pay(BigDecimal amount); }

@Component class WechatPay implements PayStrategy {
    public void pay(BigDecimal a) { ... } }
@Component class Alipay implements PayStrategy { ... }

@Component class PayService {
    private final Map<String, PayStrategy> strategies;

    public PayService(List<PayStrategy> list) {       // Spring 自动注入全部实现
        strategies = list.stream().collect(Collectors.toMap(
            s -> s.getClass().getSimpleName().replace("Pay", ""), s -> s));
    }

    public void pay(String type, BigDecimal a) {
        strategies.get(type).pay(a);                // 取代 if (type.equals("wx"))...
    }
}

🎯 面试要点

  • 策略 + 工厂 + 注册表是面试"如何消除大量 if-else"的标准答案
  • 对比模板方法:模板方法固定骨架、子类改细节;策略是整体算法可替换

3. 模板方法模式?

模板方法:父类定义算法骨架(不可变流程),把变化的步骤留给子类实现(钩子方法)。"好莱坞原则"的体现:父类调子类。

应用:AbstractQueuedSynchronizer(AQS)——acquire 流程固定,tryAcquire 留给子类(见并发模块);Spring 的 JdbcTemplate(execute 骨架)、AbstractApplicationContext.refresh()。

模板方法骨架
abstract class DataParser {
    public final void parse(String file) {   // 骨架固定(final 防改写)
        open(file);
        for (String line : readLines()) process(line);  // 流程固定
        close();
    }
    protected abstract void open(String file);    // 变化点:子类实现
    protected abstract void process(String line);
    protected void close() {}                        // 钩子:默认空实现
}

🎯 面试要点

  • 和策略模式的对比是常考点:骨架固定 vs 算法互换
  • AQS 是模板方法的教科书案例——结合并发模块一起答

4. 责任链模式?应用场景?

责任链:多个处理器串联,请求沿链传递直到被处理。解耦"发起者"与"处理者",动态编排处理顺序。

应用:

责任链骨架
abstract class Handler {
    private Handler next;
    public Handler setNext(Handler next) { this.next = next; return next; }
    public final void handle(Request req) {
        if (canHandle(req)) doHandle(req);     // 能处理就处理
        else if (next != null) next.handle(req);  // 否则交给下一个
    }
    protected abstract boolean canHandle(Request r);
    protected abstract void doHandle(Request r);
}

🎯 面试要点

  • 链与列表的区别:链上的处理"顺序语义"(前置校验→业务→后置)
  • 职责链模式 vs 装饰器:链是横向传递(请求移动),装饰器是纵向包裹(调用嵌套)

5. 状态模式?

状态模式:对象的行为随内部状态变化而变化——把每个状态封装成类,状态切换 = 切换状态对象。消除大量 if (state == ...) 分支。

应用:订单状态机(待支付→已支付→已发货→已收货)、工作流引擎、游戏角色状态。

🎯 面试要点

  • 状态 vs 策略:策略是"算法互换"(外部选择);状态是"内部状态驱动"(状态自动迁移)——结构相似,语义不同
  • 状态机是高频场景题:电商订单状态流转,答"状态模式 + 状态机表(当前状态/事件/动作/下一状态)"

🎤 常见面试追问

  1. 观察者模式和发布-订阅的区别?——观察者:主题直接持有观察者列表(紧一点);发布-订阅:中间有消息代理/事件总线(完全解耦)。Spring 事件本质是观察者(ApplicationEventPublisher 直接持有监听器列表,没有独立的 broker 进程);只有 MQ 这种经过消息代理中转的才是发布-订阅。
  2. 大量 if-else 怎么用策略模式消除?——每个分支抽成策略类 + 注册表(Map<类型, 策略>,Spring 自动注入 List 转 Map)——if (type==A) 变成 strategies.get(type).do()。
  3. 模板方法和策略模式的区别?——模板方法:父类定骨架、子类改细节(继承,流程固定);策略:整体算法可替换(组合,客户端选)。AQS 是模板方法的教科书。
  4. 责任链模式的应用?——Filter 链、Spring MVC 拦截器链、Netty Pipeline、审批流。请求沿链传递直到被处理,可中断。
  5. 状态模式和策略模式的区别?——策略:外部选算法(行为不变);状态:内部状态驱动行为自动切换(状态机)。订单状态流转用状态模式。

📖 名词解释(本页术语)

术语 大白话解释
观察者模式对象状态变化时自动通知所有订阅者(公众号发文章,订阅者都收到)。Spring 事件、MQ 订阅是它的应用。
策略模式定义一族算法各自封装、运行时互换(支付方式、排序算法)。消除 if-else 的标配。
模板方法父类定义算法骨架(final),变化步骤留给子类(钩子方法)。AQS、JdbcTemplate 都是。
责任链模式多个处理器串联,请求沿链传递直到被处理。Filter/拦截器/Netty Pipeline。
状态模式行为随内部状态变化:每个状态一个类,状态切换=换状态对象。订单状态机用它。
命令模式把操作封装成对象(可排队/可撤销)。Runnable 就是命令对象。
迭代器模式统一遍历方式(hasNext/next),Java 集合的 for-each 就是它。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。