本期内容为自己总结归档,共分6章,本人遇到过的面试问题会重点标记。
(若有任何疑问,可在评论区告诉我,看到就回复)
第一章:设计模式为什么重要?
1.1 一个真实世界的困境:电商订单系统的演进
让我们从一个简单的电商订单系统开始。最初的版本非常简单:
java
public class Order {
private double amount;
public double calculateTotal() {
return amount; // 最初只有商品价格
}
}
随着业务发展,我们需要添加运费:
java
public class Order {
private double amount;
private double shippingFee;
public double calculateTotal() {
return amount + shippingFee;
}
}
接着要添加税费、优惠券、会员折扣...
java
public class Order {
private double amount;
private double shippingFee;
private double tax;
private double couponDiscount;
private boolean isVIP;
public double calculateTotal() {
double total = amount + shippingFee + tax;
if (couponDiscount > 0) {
total -= couponDiscount;
}
if (isVIP) {
total *= 0.9; // VIP 9折
}
return total;
}
}
这就是典型的"代码坏味道"------每当有新需求时,我们都在不断修改同一个类,添加更多的条件判断。这种代码有几个严重问题:
-
违反开闭原则:每次修改都需要直接修改源代码
-
难以测试:各种条件组合导致测试用例指数级增长
-
难以维护:一个地方的修改可能影响看似无关的功能
1.2 设计模式的救赎:重构后的优雅设计
如果我们应用设计模式来重构这个系统,会变成什么样?
java
// 策略模式:不同的折扣策略
public interface DiscountStrategy {
double applyDiscount(double amount);
}
public class VIPDiscount implements DiscountStrategy {
public double applyDiscount(double amount) {
return amount * 0.9;
}
}
public class CouponDiscount implements DiscountStrategy {
private double couponValue;
public double applyDiscount(double amount) {
return amount - couponValue;
}
}
// 装饰器模式:动态添加费用
public abstract class OrderDecorator {
protected Order order;
public OrderDecorator(Order order) {
this.order = order;
}
public abstract double calculateTotal();
}
public class ShippingFeeDecorator extends OrderDecorator {
public ShippingFeeDecorator(Order order) {
super(order);
}
public double calculateTotal() {
return order.calculateTotal() + 10.0; // 基础运费
}
}
通过这样的重构,我们获得了:
-
可扩展性:添加新折扣类型不需要修改现有代码
-
可测试性:每个策略都可以独立测试
-
可维护性:每个类职责单一,易于理解和修改
1.3 设计模式的核心价值
设计模式的价值远不止于代码组织,它还在以下几个层面带来深远影响:
1.3.1 提升沟通效率
想象两个开发者之间的对话:
没有设计模式:
"我需要一个类,这个类只能创建一个实例,全局访问,还要考虑多线程安全..."
有设计模式:
"这里用单例模式。"
设计模式提供了共享的词汇表,让开发者可以用更少的词汇表达更复杂的概念。
1.3.2 加速学习曲线
当我加入一个新的团队时,如果看到代码中使用了熟悉的模式,我能更快地理解系统架构。设计模式就像是软件架构的通用设计模式语言,降低了新人理解系统的门槛。
1.3.3 避免重复造轮子
很多设计问题是通用的。设计模式记录了经过验证的解决方案,让我们可以直接站在巨人的肩膀上,而不是从零开始摸索。
第二章:设计模式全景概览
2.1 设计模式的起源:GOF 23种模式
设计模式的概念最早出现在建筑领域。建筑师克里斯托弗·亚历山大在《建筑的永恒之道》中提出:许多建筑问题都有共同的解决方案模式。
1994年,Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四位作者(被称为"四人帮"或GOF)出版了《设计模式:可复用面向对象软件的基础》,系统地提出了23种设计模式。
这23种模式被分为三大类,下面是它们的全景图:

2.2 创建型模式:优雅地创建对象
创建型模式专注于对象的创建机制,帮助我们更灵活、更合理地创建对象实例。
核心思想:解耦对象的创建与使用
想象一个汽车制造厂。司机不需要知道汽车是如何制造的,只需要知道如何驾驶。同样,在软件中,创建型模式将对象的创建过程封装起来,让使用者无需关心创建细节。

⭐常用创建型模式对比:
| 模式 | 解决的核心问题 | 典型应用场景 |
|---|---|---|
| 单例模式 | 确保一个类只有一个实例,并提供全局访问点 | 数据库连接池、配置管理器、日志记录器 |
| 工厂方法 | 将对象创建延迟到子类,让子类决定创建什么对象 | 跨平台UI组件、支付方式选择 |
| 抽象工厂 | 创建一系列相关或依赖的对象族 | GUI库、跨数据库访问层 |
| 建造者 | 分离复杂对象的构建过程和表示 | 创建复杂对象(如SQL查询、HTTP请求) |
| 原型模式 | 通过复制现有对象来创建新对象 | 游戏中的敌人复制、成本较高的对象创建 |
2.3 结构型模式:构建灵活的结构
结构型模式关注如何将类或对象组合成更大的结构,同时保持结构的灵活和高效。
核心思想:组合优于继承
在面向对象设计中,继承是最常见的代码复用方式。但过度使用继承会导致:
-
类层次结构过于复杂
-
父类的修改会影响所有子类
-
编译时确定的静态关系,缺乏灵活性
结构型模式提倡使用组合来替代继承,提供更大的灵活性。

⭐常用结构型模式对比:
| 模式 | 解决的核心问题 | 典型应用场景 |
|---|---|---|
| 适配器模式 | 让不兼容的接口能够协同工作 | 旧系统集成、第三方库适配 |
| 装饰器模式 | 动态地给对象添加额外职责 | Java I/O流、中间件链 |
| 代理模式 | 为其他对象提供一种代理以控制访问 | 远程代理、虚拟代理、保护代理 |
| 外观模式 | 为复杂子系统提供统一入口 | 框架API设计、简化复杂调用 |
| 组合模式 | 以树形结构处理对象集合 | 文件系统、GUI组件树 |
2.4 行为型模式:对象间的协作与职责分配
行为型模式关注对象之间如何交互、如何分配职责,以及算法的封装。
核心思想:将变化的部分封装起来
软件中最难维护的部分就是经常变化的部分。行为型模式将这些变化封装在独立的类中,使系统更加稳定和灵活。

⭐常用行为型模式对比:
| 模式 | 解决的核心问题 | 典型应用场景 |
|---|---|---|
| 观察者模式 | 对象间的一对多依赖关系 | 事件处理系统、MVC架构 |
| 策略模式 | 算法的封装与替换 | 支付方式、排序算法 |
| 模板方法 | 定义算法的骨架,延迟步骤实现 | 框架钩子、工作流程 |
| 责任链 | 请求的链式处理 | 过滤器链、审批流程 |
| 状态模式 | 对象状态改变时改变行为 | 订单状态机、游戏角色状态 |
2.5 ⭐设计模式的基石:SOLID原则
在深入学习具体模式之前,理解SOLID原则至关重要。这些原则是设计模式的理论基础和指导思想。

2.5.1 单一职责原则(SRP)
一个类应该只有一个引起它变化的原因。
优点:
- 功能单一,职责清晰。
- 增强可读性,方便维护
缺点:- 拆分的太详细,类的数量会急剧增加。
- 职责的的度量没有统一的标准,需要根据项目实现情况而定。
违反SRP的例子:
java
public class UserService {
// 用户验证
public boolean validateUser(String username, String password) { ... }
// 数据库操作
public void saveUser(User user) { ... }
// 发送邮件
public void sendEmail(User user, String message) { ... }
}
遵循SRP的重构:
java
public class UserValidator { ... }
public class UserRepository { ... }
public class EmailService { ... }
2.5.2 开闭原则(OCP)
软件实体应该对扩展开放,对修改关闭。
这是我们文章开头订单系统的核心问题。通过策略模式和装饰器模式,我们成功地遵循了OCP原则:当需要新的折扣类型或费用类型时,我们扩展新的类,而不是修改现有代码。
2.5.3 里氏替换原则(LSP)
子类型必须能够替换它们的父类型。
这意味着任何父类出现的地方,子类都应该能够无缝替换。这个原则确保了继承关系的正确使用。
违反LSP的例子:
java
class Rectangle {
protected int width, height;
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
}
class Square extends Rectangle {
// 正方形要求宽高相等
@Override
public void setWidth(int width) {
super.setWidth(width);
super.setHeight(width);
}
@Override
public void setHeight(int height) {
super.setWidth(height);
super.setHeight(height);
}
}
这里Square无法完美替换Rectangle,因为设置宽高的行为不一致。
2.5.4 接口隔离原则(ISP)
客户端不应该被迫依赖它们不使用的接口。
将庞大的接口拆分为更小、更具体的接口,让客户端只需知道它们真正需要的方法。
2.5.5 依赖倒置原则(DIP)
高层模块不应该依赖低层模块,两者都应该依赖抽象。
这是实现松耦合的关键。通过依赖抽象(接口或抽象类),而不是具体实现,我们的系统变得更加灵活。
第三部分:如何学习设计模式
3.1 学习路径建议
学习设计模式不是一蹴而就的过程。我建议按照以下路径循序渐进:

3.2 避免常见误区
在学习设计模式的过程中,有几个常见误区需要警惕:
误区1:为了使用模式而使用模式
这是最常见的错误。设计模式是解决特定问题的工具,不是必须遵循的教条。如果简单代码就能解决问题,就不要强行使用模式。
反例:
java
// 过度设计的单例模式
public class SimpleConfig {
private static volatile SimpleConfig instance;
private static final Object lock = new Object();
private SimpleConfig() {
// 私有构造器
}
public static SimpleConfig getInstance() {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new SimpleConfig();
}
}
}
return instance;
}
// 实际上只有一个简单的配置项
private String config = "default";
public String getConfig() { return config; }
}
如果配置确实简单且不需要延迟初始化,直接使用静态变量可能更清晰。
误区2:过度设计,过早优化
"将来可能需要"是一个危险的信号。遵循YAGNI原则(You Ain't Gonna Need It),只有在真正需要时才引入复杂性。
误区3:死记硬背模式结构
理解模式的意图 和适用场景比记住UML图更重要。同样的模式在不同场景下可能有不同的实现方式。
第四部分:从理论到实践:设计模式在真实项目中的应用
让我们看一个综合案例,展示多个设计模式如何协同工作。
4.1 场景:电商平台通知系统
需求:电商平台需要向用户发送各种通知(订单确认、发货通知、促销信息),支持多种渠道(短信、邮件、App推送),并且可以灵活添加新渠道。
初始设计(存在问题的版本):
java
public class NotificationService {
public void send(String message, String type, String channel) {
if ("order".equals(type)) {
if ("sms".equals(channel)) {
// 发送订单短信
System.out.println("发送订单短信: " + message);
} else if ("email".equals(channel)) {
// 发送订单邮件
System.out.println("发送订单邮件: " + message);
}
// 更多条件判断...
} else if ("promotion".equals(type)) {
// 更多条件判断...
}
// 每增加一种类型或渠道,就要添加更多if-else
}
}
重构后的设计(应用多种模式):
java
// 策略模式:不同的消息类型
public interface MessageType {
String format(String content);
}
public class OrderMessage implements MessageType {
public String format(String content) {
return "[订单] " + content;
}
}
public class PromotionMessage implements MessageType {
public String format(String content) {
return "[促销] " + content;
}
}
// 策略模式:不同的发送渠道
public interface Channel {
void send(String formattedMessage);
}
public class SMSChannel implements Channel {
public void send(String formattedMessage) {
System.out.println("通过短信发送: " + formattedMessage);
}
}
public class EmailChannel implements Channel {
public void send(String formattedMessage) {
System.out.println("通过邮件发送: " + formattedMessage);
}
}
// 工厂模式:创建不同类型的消息处理器
public class MessageProcessorFactory {
public static MessageProcessor createProcessor(String type) {
switch (type) {
case "order":
return new OrderProcessor();
case "promotion":
return new PromotionProcessor();
default:
throw new IllegalArgumentException("未知的消息类型");
}
}
}
// 观察者模式:当需要添加新功能时(如日志记录、数据统计)
public abstract class MessageObserver {
public abstract void onMessageSent(String message);
}
public class LoggingObserver extends MessageObserver {
public void onMessageSent(String message) {
System.out.println("记录日志: " + message);
}
}
// 主服务类,现在变得非常简洁
public class NotificationService {
private List<Channel> channels = new ArrayList<>();
private List<MessageObserver> observers = new ArrayList<>();
public void send(String content, String type) {
MessageProcessor processor = MessageProcessorFactory.createProcessor(type);
String formattedMessage = processor.process(content);
for (Channel channel : channels) {
channel.send(formattedMessage);
}
for (MessageObserver observer : observers) {
observer.onMessageSent(formattedMessage);
}
}
public void addChannel(Channel channel) {
channels.add(channel);
}
public void addObserver(MessageObserver observer) {
observers.add(observer);
}
}
这个重构后的设计具有以下优点:
-
符合开闭原则:添加新消息类型或新渠道只需添加新类,无需修改现有代码
-
单一职责:每个类只负责一个特定功能
-
可测试性:每个组件都可以独立测试
-
灵活性:可以动态添加/移除渠道和观察者
总结:开始设计模式之旅
设计模式不是高深莫测的理论,而是解决实际开发问题的实用工具集。总结一下:
-
为什么需要设计模式:解决代码复杂性,提高可维护性和可扩展性
-
设计模式的分类:创建型、结构型、行为型三大类别
-
SOLID原则:设计模式的理论基础
-
如何学习设计模式:从原则到实践,避免常见误区
记住 ,设计模式的最终目标不是写出"符合模式"的代码,而是写出清晰、可维护、可扩展的代码。模式只是达到这个目的的手段之一。
在接下来的系列文章中,我们将深入探讨每个核心模式,通过更多真实案例帮助你真正掌握这些强大的工具。