设计模式
面试中关于23种设计模式的问题,通常不是让你一口气背出所有模式,而是考察你的**理解深度、应用经验以及沟通能力**。一个优秀的回答应该是有结构、有重点、有例子的。
一、 总体回答策略(黄金开头)
建议开场白:
“设计模式是前辈们总结的、针对特定问题的、可复用的解决方案。它就像武术的招式,能提高代码的复用性、可维护性和可扩展性。GoF的23种设计模式主要分为三大类:创建型、结构型和行为型。
在工作中,我不只是死记硬背,而是把它们当作工具。比如,在写框架或进行代码重构时,我会考虑用哪种模式能让代码更清晰。下面我按分类简单介绍一下,并重点说说我项目中用得最多的几种。
二、 分类概述(展示知识体系)
你可以快速带过分类,展示你对整个体系的宏观理解。
1. **创建型模式(5种):** 关注对象的创建过程,将对象的创建与使用分离。
* *例子:* 单例、工厂方法、抽象工厂、建造者、原型。
2. **结构型模式(7种):** 关注类和对象的组合,通过继承或组合来构建更大的结构。
* *例子:* 适配器、装饰器、代理、外观、桥接、组合、享元。
3. **行为型模式(11种):** 关注对象之间的交互与职责分配。
* *例子:* 策略、模板方法、观察者、责任链、迭代器、命令、状态、备忘录、访问者、中介者、解释器。
三、 重点模式详解(展示深度)
面试官最感兴趣的是你**真正用过**的模式。以下是最最核心的5-6种,建议准备好它们的**定义、核心思想、使用场景(最好结合你的项目)和优缺点**。
1. 单例模式 (Singleton)
* **问什么:** 最基础的,考察对全局状态和多线程的理解。
* **怎么答:**
* **定义:** 确保一个类只有一个实例,并提供一个全局访问点。
* **场景:** 线程池、缓存、日志对象、数据库连接池、Spring Bean默认作用域。
* **实现方式:** 懒汉式(注意线程安全问题,需要双重检查锁或静态内部类)、饿汉式。**重点提一下双重检查锁(Double-Checked Locking)和 volatile 关键字的作用(防止指令重排)。**
#### 2. 工厂模式 (Factory Method & Abstract Factory)
* **问什么:** 考察解耦和创建对象的能力。
* **怎么答:**
* **简单工厂:** 一个工厂类根据参数返回不同产品。(不算GoF 23种,但常问)
* **工厂方法:** 定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。适用于一个产品等级结构。
* **抽象工厂:** 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。适用于多个产品等级结构(产品族)。
* **场景:** 在项目中,比如要对接不同的支付方式(微信、支付宝),可以用抽象工厂或工厂方法来封装不同支付渠道的初始化逻辑。
3. 策略模式 (Strategy)
* **问什么:** 考察消除`if-else`或`switch`的能力,是行为型模式的重点。
* **怎么答:**
* **定义:** 定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。算法的变化独立于使用算法的客户。
* **场景:**
1. 促销活动,根据会员等级计算不同折扣。
2. 电商平台,根据节日采用不同的促销策略。
* **结合项目:** “在我们项目中,有一个价格计算模块,之前充满了大量的 `if-else`。后来我用策略模式重构了它,将不同计价规则(如标准价、会员价、促销价)封装成独立的策略类,通过一个上下文类来调用。这样不仅代码清爽了,新增一种计价方式也只需新增一个类,完全符合开闭原则。”
4. 观察者模式 (Observer)
* **问什么:** 考察事件驱动和消息通知机制。
* **怎么答:**
* **定义:** 定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
* **场景:**
1. 消息队列、事件总线。
2. Spring中的事件监听机制(`ApplicationListener`)。
3. 前端JavaScript的事件监听。
* **结合项目:** “在项目中,当用户下单成功后,需要触发一系列动作(发送短信、更新库存、发放积分)。如果用同步调用,代码耦合度高,且下单主流程耗时长。我们引入了观察者模式(或类似Guava的EventBus),下单成功后只发布一个‘订单创建成功事件’,由各个监听器异步处理后续任务,大大提高了系统的响应速度和可扩展性。”
5. 模板方法模式 (Template Method)
* **问什么:** 考察对框架设计的理解,复用代码的能力。
* **怎么答:**
* **定义:** 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变算法结构即可重定义算法的某些特定步骤。
* **场景:**
1. 在Spring中,`JdbcTemplate`,它定义了数据库操作的模板(打开连接、执行语句、处理结果、关闭连接),而具体的SQL语句和结果映射由子类或回调实现。
2. 制作饮料的流程(泡茶、冲咖啡),步骤固定(烧水、冲泡、倒杯、加料),但具体冲泡物和加料不同。
6. 代理模式 (Proxy)
* **问什么:** 考察AOP(面向切面编程)的底层思想。
* **怎么答:**
* **定义:** 为另一个对象提供一个替身或占位符以控制对这个对象的访问。
* **分类:** 静态代理、动态代理(JDK动态代理、CGLib)。
* **场景:**
1. Spring AOP:通过代理来实现事务管理、日志、权限控制。
2. 远程代理(RMI)。
3. 虚拟代理(延迟加载大图)。
* **结合项目:** “我们使用Spring声明式事务 `@Transactional`,其底层就是通过代理实现的。当我们调用带此注解的方法时,实际上调用的是代理对象的方法,代理对象会先开启事务,然后执行目标方法,最后根据异常情况提交或回滚事务。” 四、 终极问题:设计模式在项目中的实际应用
这是面试的加分项。你需要准备一个**真实的故事**。
**回答模板:**
> “在我之前负责的XX项目中,我们遇到了一个问题:[描述问题,如:业务逻辑复杂,if-else太多;或者 需要对接多个外部系统,代码重复率高]。
> 为了解决这个问题,我们引入了 **策略模式 + 工厂模式**(或其他组合)。
> **具体做法是:**
> 1. 定义了一个统一的策略接口 `Handler`。
> 2. 针对不同的业务类型(如A、B、C),分别创建了 `AHandler`、`BHandler`、`CHandler` 实现该接口。
> 3. 创建了一个策略工厂 `HandlerFactory`,使用一个 `Map` 来维护 业务类型 -> Handler Bean 的映射关系(利用Spring的 `ApplicationContextAware` 在启动时自动注入)。
> 4. 业务调用时,只需要通过工厂获取对应的 `Handler`,然后执行即可。
> **最终效果:**
> * 代码从原来数百行的 `switch` 变成了清晰的结构。
> * 扩展性极强,新增D类型业务时,只需新增 `DHandler`,无需修改原有代码,符合开闭原则。
> * 每个 `Handler` 独立,也方便进行单元测试。”
五、 总结与避坑指南
1. **不要过度设计:** 主动告诉面试官,“设计模式虽好,但也要避免滥用,否则会增加系统的复杂性。我们是在代码确实需要重构或扩展时才引入合适的模式。”
2. **区分容易混淆的模式:**
* **策略 vs 状态:** 策略是主动选择的算法,状态是对象内部状态变化导致行为改变。
* **工厂 vs 建造者:** 工厂立即返回产品,建造者负责一步步构建复杂对象,最后 `build`。
3. **结合框架:** 尽量将设计模式和你熟悉的框架(如Spring、MyBatis)联系起来。例如:Spring中用到了**工厂**(BeanFactory)、**代理**(AOP)、**模板方法**(JdbcTemplate)、**单例**(Bean Scope)、**观察者**(ApplicationEvent)等。
**祝面试顺利!** 按照这个结构准备,既能展现你的广度,也能体现你的深度和实战经验。
浙公网安备 33010602011771号