本文目录导读:
这是一个很核心的问题,设计模式不是“银弹”,也不是一开始就要生搬硬套的。
简单说:设计模式是特定场景下,解决重复性设计问题的成熟模板。 应用的核心不是“我要用哪个模式”,而是“我遇到了什么问题,哪个模式最能优雅地解决它”。
下面我从识别问题->选择模式->实践落地 三个步骤,结合具体代码案例来说明。
第一步:识别症状(判断是否需要用模式)
不要为了用模式而用模式,当你写代码时出现以下 “坏味道” ,就是考虑模式的时候:
- 代码臃肿,难以扩展:一个
if-else或switch有十几层,新增一个类型就要改所有地方。 - 对象创建复杂:需要创建一组相关或依赖的对象,且创建逻辑经常变。
- 对象间耦合过紧:一个对象的改动,导致N个对象跟着一起改(牵一发动全身)。
- 需要兼容多个接口或旧系统:接口不匹配,又不能改对方代码。
第二步:匹配场景(常用模式的应用案例)
这里选几个最高频、最实用的模式,结合真实场景说明。
策略模式(Strategy Pattern)—— 消灭大量 if-else
- 场景:电商系统计算订单价格,普通会员打9折,VIP打8折,钻石打7折,且后期会不断增加会员等级。
- 坏味道:
if (user.isVip) {...} else if (user.isDiamond)... - 模式应用:
// 1. 定义策略接口
public interface DiscountStrategy {
BigDecimal apply(BigDecimal price);
}
// 2. 实现具体策略
public class NormalDiscount implements DiscountStrategy {
public BigDecimal apply(BigDecimal price) { return price.multiply(new BigDecimal("0.9")); }
}
public class VipDiscount implements DiscountStrategy {
public BigDecimal apply(BigDecimal price) { return price.multiply(new BigDecimal("0.8")); }
}
// 3. 上下文类(使用策略)
public class OrderService {
private DiscountStrategy strategy;
public OrderService(DiscountStrategy strategy) { // 构造器或setter注入
this.strategy = strategy;
}
public BigDecimal checkout(BigDecimal price) {
return strategy.apply(price); // 不用if-else,直接委托
}
}
// 4. 客户端调用
// 用户登录后,根据等级创建策略
DiscountStrategy ds = new VipDiscount();
OrderService order = new OrderService(ds);
order.checkout(price);
工厂方法模式(Factory Method)—— 解耦对象创建
- 场景:日志系统,需要记录到文件、数据库、或远程服务器。
- 坏味道:
LogLogger logger = new FileLogger()在多个地方硬编码创建。 - 模式应用:
// 1. 产品接口
public interface Logger { void log(String msg); }
public class FileLogger implements Logger { ... }
public class DbLogger implements Logger { ... }
// 2. 工厂接口(工厂方法)
public abstract class LoggerFactory {
public abstract Logger createLogger(); // 延迟到子类实现
public void logSomething(String msg) {
Logger logger = createLogger(); // 调用工厂方法
logger.log(msg);
}
}
// 3. 具体工厂
public class FileLoggerFactory extends LoggerFactory {
public Logger createLogger() { return new FileLogger(); }
}
public class DbLoggerFactory extends LoggerFactory {
public Logger createLogger() { return new DbLogger(); }
}
// 4. 使用
LoggerFactory factory = new FileLoggerFactory();
factory.logSomething("..."); // 客户端只依赖抽象工厂,不依赖具体Logger类
观察者模式(Observer)—— 解耦发布-订阅
- 场景:微信支付成功后,需要:1. 发送短信,2. 发放积分,3. 推送通知,可能后续还要加“打印小票”。
- 坏味道:支付成功后,在一个方法里依次调用
sendSms(),addPoints(),pushNotify(),新增功能就要改这个方法。 - 模式应用:
// 1. 观察者接口
public interface PaymentObserver {
void afterPaid(Order order);
}
// 2. 具体观察者
public class SmsObserver implements PaymentObserver { ... }
public class PointsObserver implements PaymentObserver { ... }
// 3. 被观察者(事件源)
public class PaymentService {
private List<PaymentObserver> observers = new ArrayList<>();
public void addObserver(PaymentObserver observer) { observers.add(observer); }
public void pay(Order order) {
// ... 核心支付逻辑
for (PaymentObserver observer : observers) {
observer.afterPaid(order); // 通知所有观察者
}
}
}
// 4. 使用
PaymentService service = new PaymentService();
service.addObserver(new SmsObserver());
service.addObserver(new PointsObserver());
service.pay(order); // 支付成功后自动执行所有订阅者
适配器模式(Adapter)—— 兼容旧接口
- 场景:系统之前用MySQL数据库,现在要换成Oracle,但旧代码里到处都是
MySQLUtil.query()。 - 坏味道:改所有调用处,风险高。
- 模式应用:
// 1. 目标接口(我们想要的)
public interface Database {
void query(String sql);
}
// 2. 被适配者(旧系统或第三方库)
public class OracleDB {
public void executeQuery(String sql) { ... }
}
// 3. 适配器
public class OracleAdapter implements Database {
private OracleDB oracleDB;
public OracleAdapter(OracleDB oracleDB) { this.oracleDB = oracleDB; }
@Override
public void query(String sql) {
// 做必要的转换,然后调用被适配者的方法
oracleDB.executeQuery(sql);
}
}
// 4. 使用(客户端只依赖Database接口)
Database db = new OracleAdapter(new OracleDB());
db.query("SELECT * FROM users"); // 代码不用改
第三步:落地原则(避免滥用)
- KISS(Keep It Simple, Stupid):如果2个类就能解决问题,不要为了用模式而引入5个类,模式解决的是变化问题,如果未来不可能变化,直接用if-else更简单。
- 组合优于继承:优先用接口+组合(如策略模式),而不是大而全的类继承,继承容易造成类爆炸。
- 先有重构,后有模式:不要一开始就设计满屏模式,先写能跑的代码,当发现第二次需要修改类似逻辑时,再重构引入模式,这就是重构 -> 模式。
- 熟读《设计模式》GoF:23种模式不是都要背,但创建型(单例、工厂、建造者)、结构型(适配器、装饰器、代理)、行为型(策略、观察者、模板方法) 这9个是最常用的,要能画出类图和讲出场景。
一个决策流程
当你写代码卡住时,问自己:
- 这段代码为什么难改? —— 因为有大量if-else?(考虑策略模式),因为对象创建太散乱?(考虑工厂模式)。
- 可能的变化在哪里? —— 算法经常变?把算法封装成策略,对象种类经常变?把创建过程封装成工厂。
- 引入模式后,代码是更清晰了还是更复杂了? —— 如果团队其他成员看不懂,或者导致Debug困难,说明过度设计了。
一句话应用心法:没有最好的模式,只有最合适的模式。 把模式当作工具箱里的工具,而不是蓝图,当你写出了一段自己都觉得“这以后肯定要改死”的代码时,就是打开工具箱的时候了。