策略模式与模板方法
策略模式和模板方法都是行为型设计模式,用于封装算法和行为。
一、策略模式
1. 核心概念
策略模式定义一系列算法,把它们封装起来,并使它们可以相互替换。
┌─────────────────────────────────────┐
│ Context │
│ ┌─────────────────────────────┐ │
│ │ - strategy: Strategy │ │
│ │ + setStrategy() │ │
│ │ + executeStrategy() │ │
│ └─────────────────────────────┘ │
└──────────────┬──────────────────────┘
│ 持有
▼
┌─────────────────────────────────────┐
│ <<interface>> │
│ Strategy │
│ + algorithm(): void │
└──────────────┬──────────────────────┘
│ 实现
┌─────────┼─────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│StrategyA│ │StrategyB│ │StrategyC│
└────────┘ └────────┘ └────────┘2. 实现
// 策略接口
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
// 具体策略:支付宝
public class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("支付宝支付: " + amount);
}
}
// 具体策略:微信
public class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("微信支付: " + amount);
}
}
// 具体策略:银行卡
public class BankCardStrategy implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("银行卡支付: " + amount);
}
}
// 上下文
public class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void pay(BigDecimal amount) {
if (strategy == null) {
throw new IllegalStateException("未设置支付方式");
}
strategy.pay(amount);
}
}
// 使用
PaymentContext context = new PaymentContext();
context.setStrategy(new AlipayStrategy());
context.pay(new BigDecimal("100.00"));
context.setStrategy(new WechatPayStrategy());
context.pay(new BigDecimal("200.00"));3. 结合枚举使用
public enum PaymentType {
ALIPAY {
@Override
public PaymentStrategy getStrategy() {
return new AlipayStrategy();
}
},
WECHAT {
@Override
public PaymentStrategy getStrategy() {
return new WechatPayStrategy();
}
},
BANK_CARD {
@Override
public PaymentStrategy getStrategy() {
return new BankCardStrategy();
}
};
public abstract PaymentStrategy getStrategy();
}
// 使用
PaymentStrategy strategy = PaymentType.ALIPAY.getStrategy();
strategy.pay(new BigDecimal("100.00"));4. Spring 中的应用
// 策略接口
public interface FileParser {
String parse(String content);
String getType();
}
// 具体策略
@Component
public class JsonParser implements FileParser {
@Override
public String parse(String content) {
return "JSON解析: " + content;
}
@Override
public String getType() {
return "json";
}
}
@Component
public class XmlParser implements FileParser {
@Override
public String parse(String content) {
return "XML解析: " + content;
}
@Override
public String getType() {
return "xml";
}
}
// 策略工厂
@Component
public class FileParserFactory {
private final Map<String, FileParser> parsers;
public FileParserFactory(List<FileParser> parserList) {
parsers = parserList.stream()
.collect(Collectors.toMap(FileParser::getType, p -> p));
}
public FileParser getParser(String type) {
return parsers.get(type);
}
}
// 使用
@Service
public class FileService {
@Autowired
private FileParserFactory parserFactory;
public String parseFile(String type, String content) {
FileParser parser = parserFactory.getParser(type);
return parser.parse(content);
}
}二、模板方法模式
1. 核心概念
模板方法定义算法骨架,将某些步骤延迟到子类实现。
┌─────────────────────────────────────┐
│ AbstractClass (抽象类) │
│ + templateMethod(): void │ ← 模板方法(final)
│ # primitiveOperation1(): void │ ← 抽象方法
│ # primitiveOperation2(): void │ ← 抽象方法
│ # hookMethod(): void │ ← 钩子方法(可选)
└──────────────┬──────────────────────┘
│ 继承
┌─────────┴─────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ConcreteClassA│ │ConcreteClassB│
└──────────────┘ └──────────────┘2. 实现
// 抽象类
public abstract class DataProcessor {
// 模板方法(final 防止子类重写)
public final void process() {
readData();
if (validateData()) { // 钩子方法控制流程
processData();
saveData();
}
logResult();
}
// 抽象方法:子类必须实现
protected abstract void readData();
protected abstract void processData();
protected abstract void saveData();
// 钩子方法:子类可选重写
protected boolean validateData() {
return true;
}
protected void logResult() {
System.out.println("处理完成");
}
}
// 具体实现:文件处理器
public class FileDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("从文件读取数据");
}
@Override
protected void processData() {
System.out.println("处理文件数据");
}
@Override
protected void saveData() {
System.out.println("保存到数据库");
}
@Override
protected boolean validateData() {
System.out.println("验证文件数据");
return true;
}
}
// 具体实现:数据库处理器
public class DatabaseDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("从数据库读取数据");
}
@Override
protected void processData() {
System.out.println("处理数据库数据");
}
@Override
protected void saveData() {
System.out.println("保存到缓存");
}
}
// 使用
DataProcessor processor = new FileDataProcessor();
processor.process();3. Spring 中的应用
JdbcTemplate:
public class UserRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public User findById(Long id) {
// JdbcTemplate 定义了查询模板
// 我们只需提供 RowMapper 实现
return jdbcTemplate.queryForObject(
"SELECT * FROM user WHERE id = ?",
new Object[]{id},
(rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
return user;
}
);
}
}HttpServlet:
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// HttpServlet.service() 是模板方法
// 根据请求类型调用 doGet/doPost 等方法
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
// ...
}
}三、两种模式对比
| 对比项 | 策略模式 | 模板方法模式 |
|---|---|---|
| 目的 | 算法可替换 | 算法骨架固定 |
| 变化点 | 整体算法 | 算法中的某些步骤 |
| 实现方式 | 组合 | 继承 |
| 扩展性 | 新增策略类 | 新增子类 |
| 运行时切换 | 支持 | 不支持 |
四、实际应用场景
1. 策略模式应用
- 支付方式选择
- 排序算法选择
- 压缩算法选择
- 验证规则选择
2. 模板方法应用
- 数据处理流程
- 工作流引擎
- 框架扩展点
- 测试用例模板
3. 组合使用
// 订单处理流程(模板方法) + 支付方式(策略模式)
public abstract class OrderProcessor {
private PaymentStrategy paymentStrategy;
public final void process(Order order) {
validateOrder(order);
calculatePrice(order);
pay(order);
deliver(order);
}
protected abstract void validateOrder(Order order);
protected abstract void deliver(Order order);
private void calculatePrice(Order order) {
// 通用价格计算逻辑
}
private void pay(Order order) {
paymentStrategy.pay(order.getAmount());
}
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
}五、面试高频问题
Q1: 策略模式优缺点?
优点:
- 避免大量 if-else
- 易于扩展新策略
- 策略可复用
缺点:
- 客户端需要知道所有策略
- 策略类数量增多
Q2: 模板方法优缺点?
优点:
- 复用公共代码
- 控制子类扩展点
- 符合开闭原则
缺点:
- 增加类数量
- 继承关系复杂
Q3: 策略模式如何消除 if-else?
// 传统 if-else
public void pay(String type, BigDecimal amount) {
if ("alipay".equals(type)) {
new AlipayStrategy().pay(amount);
} else if ("wechat".equals(type)) {
new WechatPayStrategy().pay(amount);
}
}
// 策略模式
PaymentStrategy strategy = strategyFactory.getStrategy(type);
strategy.pay(amount);Q4: 模板方法的钩子方法是什么?
钩子方法是可选实现的方法,用于:
- 控制模板流程
- 提供默认实现
- 允许子类选择性扩展
Q5: Spring 哪些地方用到了这些模式?
| 模式 | Spring 应用 |
|---|---|
| 策略模式 | Resource、Validator、Parser |
| 模板方法 | JdbcTemplate、RestTemplate、TransactionTemplate |
六、最佳实践
1. 策略模式配合工厂
@Component
public class StrategyFactory {
private final Map<String, Strategy> strategies;
public StrategyFactory(List<Strategy> strategyList) {
strategies = strategyList.stream()
.collect(Collectors.toMap(Strategy::getType, s -> s));
}
public Strategy getStrategy(String type) {
return Optional.ofNullable(strategies.get(type))
.orElseThrow(() -> new IllegalArgumentException("未知类型"));
}
}2. 模板方法配合函数式接口
public class DataProcessor {
public void process(Runnable reader, Consumer<String> processor, Runnable saver) {
reader.run();
// 处理逻辑
saver.run();
}
}
// 使用 Lambda
processor.process(
() -> readFromFile(),
data -> transform(data),
() -> saveToDb()
);3. 选择合适模式
整体算法可替换 → 策略模式
算法骨架固定、步骤可变 → 模板方法更新时间:2026年3月16日