知识模块
☕ Java 知识模块
十一、设计模式
策略模式与模板方法

策略模式与模板方法

策略模式和模板方法都是行为型设计模式,用于封装算法和行为。

一、策略模式

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日