设计模式不是"银弹",是"经验包"
在学设计模式之前要明白它到底是什么?刚入行的时候我会这么觉得:
- 代码炫技的工具
- 写代码的必须步骤
- 衡量技术水平的标准
直到我在真实项目里踩了无数坑,被过度设计的代码折磨到崩溃,才明白一件事: 很多人不是在用设计模式,而是在折磨自己、折磨团队。
百度是这么介绍设计模式的:
我想为此添加一句话:设计模式应该是解决方案,而不是目标。
单开里依接迪合
认识各种模式前,我们应该了解它的原则。设计模式的7大原则是所有模式背后的核心思想。
如果实在背不熟这7大原则及其定义,至少要熟记单一职责和开闭原则。
- 单一职责原则:一个类只做一件事,就像快递员只负责送快递。
- 开闭原则:对扩展开放,对修改关闭,就像装修房子可以自由发挥,但不能拆承重墙。
- 里氏替换原则:子类可以替换父类,就像可口可乐可以替代水。
- 依赖倒置原则:依赖抽象不依赖具体,就像插座不依赖具体电器。
- 接口隔离原则:不要依赖不需要的接口,就像你只需点餐,不用洗碗炒菜。
- 迪米特法则:只和直接朋友交流,不要和陌生人说话,就像你只和卖家交流,不用管进货渠道。
- 合成复用原则:优先使用组合而非继承,就像组装电脑自己选配件。
别背23种设计模式,掌握8个就够了
设计模式看似很多,实际也不少[旺柴]。但是我们也不需要死记硬背,按照以下的方法会轻松很多~
当我们需要设计问题时,不要先想"用什么模式",先按顺序思考:
- 你在解决什么类型的问题?
- 需要解耦什么?
- 哪些部分可能变化?需要隔离变化吗?
- 需求稳定性如何?
- 团队熟悉度如何?
1.需要创建对象/需要对象组合结构/需要对象交互,因此也按照创建型/组合型/行为型的方式分类。
2.举例几个常见的解耦结果,我需要创建对象/我的接口不兼容另外一个接口/我希望拓展一个新功能且不影响原来功能/我希望更改一个状态能影响其他状态,对应的是工厂模式/适配器模式/装饰器模式/状态模式。
3.根据变化的部分选择模式,如需要频繁切换数据库连接方式/需要切换支付方式/需要根据订单的状态决定对应逻辑,分别对应工厂模式型(创建对象的方式都改变了)/策略模式(不同支付方式不同逻辑,属于业务变了)/状态模式。
4.需求稳定则直接实现,需求频繁变化选择引入模式。人话就是静态和动态配置。
5.团队熟悉可以用复杂模式, 团队不熟悉优先用简单方案,如观察者模式与责任链模式相似,但前者简单,后者稍微复杂一些。
让我们看看具体的设计模式~
建造者模式
- 分类:创建型模式。
- 解决什么问题:当对象有多个可选参数,且需要保证构建过程的稳定性时。
- 核心思想:将复杂对象的构建与表示分离。
- 典型场景:订单构建、配置对象、SQL查询构建。
- 使用判断准则:当可选参数大于4个时推荐使用,反之则酌情使用。
// 不使用建造者:参数顺序混乱,可读性差
Order order = new Order("123", 99.99, "SUMMER20", Arrays.asList("T恤", "牛仔裤"), LocalDateTime.now());
// 使用建造者:链式调用,清晰易读
Order order = Order.builder()
.orderId("123")
.amount(99.99)
.discountCode("SUMMER20")
.items(Arrays.asList("T恤", "牛仔裤"))
.build();
工厂方法
- 分类:创建型模式。
- 解决什么问题:将对象创建逻辑封装起来,调用方不依赖具体类。
- 核心思想:定义创建对象的接口,让子类决定实例化哪个类。
- 典型场景:支付方式工厂、日志框架工厂、数据库连接工厂。
- 使用判断准则:当创建对象逻辑复杂时,内部有多个实现时推荐使用,反之则酌情使用。
// 支付接口
public interface Payment {
void pay(BigDecimal amount);
}
// 具体支付实现
public class AlipayPayment implements Payment {
@Override
public void pay(BigDecimal amount) {
// 支付宝支付逻辑
}
}
// 工厂类
public class PaymentFactory {
public static Payment createPayment(String type) {
if ("alipay".equals(type)) {
return new AlipayPayment();
}
// 可以扩展其他支付方式
throw new IllegalArgumentException("Unsupported payment type");
}
}
// 使用
Payment payment = PaymentFactory.createPayment("alipay");
payment.pay(new BigDecimal("99.99"));
适配器模式
- 分类:结构型模式。
- 解决什么问题:让接口不兼容的类能够协同工作
- 核心思想:将一个类的接口转换成客户端期望的另一个接口
- 典型场景:第三方API对接、旧系统升级、接口标准化
- 使用判断准则:接口不兼容的万能解,若接口原本就兼容则无需使用。
// 第三方支付接口(无法修改)
public class ThirdPartyPayment {
public void makePayment(String account, double amount) {
// 第三方支付逻辑
}
}
// 我们期望的支付接口
public interface Payment {
void pay(BigDecimal amount);
}
// 适配器
public class PaymentAdapter implements Payment {
private ThirdPartyPayment thirdPartyPayment;
public PaymentAdapter(ThirdPartyPayment thirdPartyPayment) {
this.thirdPartyPayment = thirdPartyPayment;
}
@Override
public void pay(BigDecimal amount) {
thirdPartyPayment.makePayment("default_account", amount.doubleValue());
}
}
// 使用
Payment payment = new PaymentAdapter(new ThirdPartyPayment());
payment.pay(new BigDecimal("99.99"));
装饰器模式
- 分类:结构型模式。
- 解决什么问题:在不修改原类的情况下,动态给对象添加功能。
- 核心思想:通过组合而非继承来扩展功能。
- 典型场景:IO流包装、日志增强、权限控制、缓存。
- 使用判断准则:添加新功能的时候是否希望修改原类。
// 基础组件
public interface TextComponent {
String getText();
}
public class PlainText implements TextComponent {
private String text;
public PlainText(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
}
// 装饰器:添加加密功能
public class EncryptedText implements TextComponent {
private TextComponent component;
public EncryptedText(TextComponent component) {
this.component = component;
}
@Override
public String getText() {
return encrypt(component.getText());
}
private String encrypt(String text) {
// 加密逻辑
return "ENCRYPTED:" + text;
}
}
// 装饰器:添加压缩功能
public class CompressedText implements TextComponent {
private TextComponent component;
public CompressedText(TextComponent component) {
this.component = component;
}
@Override
public String getText() {
return compress(component.getText());
}
private String compress(String text) {
// 压缩逻辑
return "COMPRESSED:" + text;
}
}
// 使用:可以任意组合装饰器
TextComponent text = new CompressedText(new EncryptedText(new PlainText("Hello")));
System.out.println(text.getText()); // COMPRESSED:ENCRYPTED:Hello
策略模式
- 分类:行为型模式。
- 解决什么问题:将不同的算法封装起来,使它们可以互相替换。
- 核心思想:定义算法族,分别封装,让它们可以互相替换。
- 典型场景:支付方式、排序算法、折扣计算、验证规则。
- 使用判断准则:是否有大量if-else,且希望替换。
// 策略接口
public interface DiscountStrategy {
BigDecimal calculate(BigDecimal price);
}
// 具体策略
public class NormalDiscount implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal price) {
return price;
}
}
public class VIPDiscount implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal price) {
return price.multiply(new BigDecimal("0.8"));
}
}
public class SVIPDiscount implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal price) {
return price.multiply(new BigDecimal("0.6"));
}
}
// 策略工厂
public class DiscountStrategyFactory {
public static DiscountStrategy getStrategy(String userType) {
switch (userType) {
case "vip":
return new VIPDiscount();
case "svip":
return new SVIPDiscount();
default:
return new NormalDiscount();
}
}
}
// 使用:消除if-else
DiscountStrategy strategy = DiscountStrategyFactory.getStrategy("vip");
BigDecimal finalPrice = strategy.calculate(new BigDecimal("100"));
观察者模式
- 分类:行为型模式。
- 解决什么问题:定义对象间的一对多依赖,当一个对象状态改变时,所有依赖它的对象都会收到通知。
- 核心思想:发布-订阅机制。
- 典型场景:消息推送、事件监听、GUI事件处理、Spring事件机制。
- 使用判断准则:是否需要一对多的通知行为。
// 观察者接口
public interface Observer {
void update(String message);
}
// 被观察者
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体被观察者:订单
public class Order implements Subject {
private List<Observer> observers = new ArrayList<>();
private String status;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update("订单状态变更为:" + status);
}
}
public void setStatus(String status) {
this.status = status;
notifyObservers();
}
}
// 具体观察者:邮件通知
public class EmailNotifier implements Observer {
@Override
public void update(String message) {
System.out.println("发送邮件通知:" + message);
}
}
// 具体观察者:短信通知
public class SMSNotifier implements Observer {
@Override
public void update(String message) {
System.out.println("发送短信通知:" + message);
}
}
// 使用
Order order = new Order();
order.registerObserver(new EmailNotifier());
order.registerObserver(new SMSNotifier());
order.setStatus("已支付"); // 所有观察者都会收到通知
状态模式
- 分类:行为型模式。
- 解决什么问题:允许一个对象在其内部状态改变时改变它的行为。
- 核心思想:状态决定行为。
- 典型场景:订单状态流转、工作流、游戏角色状态。
- 使用判断准则:状态数量多,且不同状态有不同逻辑。
// 状态接口
public interface OrderState {
void handle(Order order);
}
// 具体状态:待支付
public class PendingPaymentState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("处理待支付状态");
order.setState(new PaidState());
}
}
// 具体状态:已支付
public class PaidState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("处理已支付状态");
order.setState(new ShippedState());
}
}
// 具体状态:已发货
public class ShippedState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("处理已发货状态");
order.setState(new CompletedState());
}
}
// 具体状态:已完成
public class CompletedState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("订单已完成");
}
}
// 订单上下文
public class Order {
private OrderState state;
public Order() {
this.state = new PendingPaymentState();
}
public void setState(OrderState state) {
this.state = state;
}
public void process() {
state.handle(this);
}
}
// 使用
Order order = new Order();
order.process(); // 待支付 → 已支付
order.process(); // 已支付 → 已发货
order.process(); // 已发货 → 已完成
order.process(); // 已完成
职责链模式
- 分类:行为型模式。
- 解决什么问题:将请求的发送者和接收者解耦,让多个对象都有机会处理请求。
- 核心思想:形成一条处理链,请求沿着链传递,直到被处理。
- 典型场景:请假审批、Filter链、异常处理、工作流。
- 使用判断准则:有多个对象可以处理请求,需要动态指定处理顺序。
// 处理器接口
public interface Handler {
void handleRequest(Request request);
void setNextHandler(Handler handler);
}
// 抽象处理器
public abstract class AbstractHandler implements Handler {
private Handler nextHandler;
@Override
public void setNextHandler(Handler handler) {
this.nextHandler = handler;
}
@Override
public void handleRequest(Request request) {
if (canHandle(request)) {
doHandle(request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
System.out.println("无人处理该请求");
}
}
protected abstract boolean canHandle(Request request);
protected abstract void doHandle(Request request);
}
// 具体处理器:组长
public class TeamLeaderHandler extends AbstractHandler {
@Override
protected boolean canHandle(Request request) {
return request.getDays() <= 3;
}
@Override
protected void doHandle(Request request) {
System.out.println("组长审批:批准" + request.getDays() + "天假期");
}
}
// 具体处理器:经理
public class ManagerHandler extends AbstractHandler {
@Override
protected boolean canHandle(Request request) {
return request.getDays() <= 7;
}
@Override
protected void doHandle(Request request) {
System.out.println("经理审批:批准" + request.getDays() + "天假期");
}
}
// 具体处理器:总监
public class DirectorHandler extends AbstractHandler {
@Override
protected boolean canHandle(Request request) {
return request.getDays() > 7;
}
@Override
protected void doHandle(Request request) {
System.out.println("总监审批:批准" + request.getDays() + "天假期");
}
}
// 请求对象
public class Request {
private int days;
public Request(int days) {
this.days = days;
}
public int getDays() {
return days;
}
}
// 使用
Handler teamLeader = new TeamLeaderHandler();
Handler manager = new ManagerHandler();
Handler director = new DirectorHandler();
teamLeader.setNextHandler(manager);
manager.setNextHandler(director);
teamLeader.handleRequest(new Request(2)); // 组长审批
teamLeader.handleRequest(new Request(5)); // 经理审批
teamLeader.handleRequest(new Request(10)); // 总监审批
易混淆模式速查表
23种模式中,也有一些比较相似的,这个时候我们可以按照我们具体需求选择对应的模式。
策略模式 vs 状态模式
| 对比项 | 策略模式 | 状态模式 |
|---|---|---|
| 目的 | 封装算法 | 封装状态 |
| 切换方式 | 客户端主动切换 | 状态自动转换 |
| 关注点 | 行为替换 | 状态转换 |
| 典型场景 | 支付方式、排序算法 | 订单状态流转、工作流 |
| 一句话区分 | 策略是"选一个" | 状态是"流转起来" |
装饰器模式 vs 适配器模式
| 对比项 | 装饰器模式 | 适配器模式 |
|---|---|---|
| 目的 | 动态增强功能 | 转换接口 |
| 是否改变接口 | 否,保持原接口 | 是,转换成客户端期望的接口 |
| 是否改变功能 | 是,添加新功能 | 否,功能本质不变 |
| 是否可组合 | 可以多层组合 | 不可组合(一对一适配) |
| 典型场景 | IO流包装、日志增强 | 第三方API对接、旧系统兼容 |
| 一句话区分 | 装饰器是"增强功能" | 适配器是"转换接口" |
工厂方法 vs 抽象工厂
| 对比项 | 工厂方法 | 抽象工厂 |
|---|---|---|
| 目的 | 创建一种产品 | 创建产品族 |
| 层级 | 一层抽象 | 两层抽象 |
| 扩展性 | 新增产品需新增工厂 | 新增产品族需新增工厂 |
| 典型场景 | 支付方式工厂 | UI组件库(按钮+文本框) |
| 一句话区分 | 工厂方法造"一个东西" | 抽象工厂造"一套东西" |
常见错误操作
为了避免我们写出错误的设计模式,我们应该多看看别人的错误案例。
1. 为模式而模式 🔥🔥🔥
// ❌ 简单支付用两层工厂
public class PaymentFactory {
public Payment create(String type) {
if ("alipay".equals(type)) {
return new AlipayFactory().create(); // 多余的嵌套
}
}
}
2. 过早抽象 🔥🔥
// ❌ 未来可能支持4种支付,现在只用到1种
class CreditCardPayment implements PaymentStrategy {...}
class PayPalPayment implements PaymentStrategy {...} // 从未使用
class BitcoinPayment implements PaymentStrategy {...} // 从未使用
3. 模式堆砌 🔥
// ❌ 一个模块用>2种模式,加功能改>3个文件
// 组合了工厂+策略+装饰器,只为一个简单功能
public class ComplexPayment {
// 工厂创建策略
private PaymentStrategy strategy = StrategyFactory.create("vip");
// 装饰器包装
private PaymentDecorator decorated = new LoggedPayment(strategy);
// 只为了一个简单支付调用
public void pay(BigDecimal amount) {
decorated.pay(amount);
}
}
4. 全局污染 🔥🔥🔥
// ❌ 所有服务都搞单例,测试时无法隔离
public class UserService {
private static UserService instance;
private UserService() {}
public static UserService getInstance() {...}
}
5. 局部优化 🔥
// ❌ 只有3个状态,硬上状态模式
// 用if-else或状态表驱动更简单
public class OrderState {
public interface State { void handle(); }
public class PendingState implements State { ... }
public class PaidState implements State { ... }
public class ShippedState implements State { ... }
// 3个状态搞了3个类,反而不如if-else清晰
}
设计模式不是银弹,而是权衡
我的5条极简原则(建议熟记):
- 能直白就不抽象:直白的代码可能不够"优雅",但一定更好维护。
- 能少一层就不多一层:每多一层封装,就多一份认知成本。
- 新人能快速看懂,才叫好代码:代码是写给人看的,顺便给机器执行。
- 先满足业务,再谈优雅:业务都没跑通,优雅给谁看?
- 可维护 > 可装逼:这句话最难,但也最重要。真正的专业,是知道什么时候不用什么。
简单来说,好的代码就是简单易读,能扛业务。
如果这篇文章对你有帮助,点个赞收藏一下吧。有问题欢迎评论区交流~