Strategy模式(策略模式)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。这种模式让算法的变化独立于使用算法的客户端。
在实际开发中,我们经常会遇到需要根据不同条件执行不同算法的情况。比如支付系统需要支持多种支付方式(支付宝、微信、银联等),每种支付方式都有自己独特的处理逻辑。如果直接在业务代码中用if-else或switch-case来处理,会导致代码臃肿且难以维护。
提示:策略模式的核心思想不是消除条件判断,而是将条件判断从业务逻辑中抽离出来,让业务逻辑专注于自身的职责。
策略模式通常包含以下三个核心角色:
Context(上下文):
Strategy(策略接口):
ConcreteStrategy(具体策略):
plaintext复制+----------------+ +-----------------+
| Context | | Strategy |
|----------------| |-----------------|
| +strategy:Strategy |<>----| +algorithm():void |
| +contextInterface()| +-----------------+
+----------------+ ^
|
+---------------------+
| |
+-----------------+ +-----------------+
| ConcreteStrategyA| | ConcreteStrategyB|
|-----------------| |-----------------|
| +algorithm() | | +algorithm() |
+-----------------+ +-----------------+
下面是一个电商平台折扣策略的Java实现示例:
java复制// 策略接口
public interface DiscountStrategy {
double applyDiscount(double originalPrice);
}
// 具体策略:无折扣
public class NoDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double originalPrice) {
return originalPrice;
}
}
// 具体策略:固定折扣
public class FixedDiscount implements DiscountStrategy {
private double discountAmount;
public FixedDiscount(double discountAmount) {
this.discountAmount = discountAmount;
}
@Override
public double applyDiscount(double originalPrice) {
return originalPrice - discountAmount;
}
}
// 具体策略:百分比折扣
public class PercentageDiscount implements DiscountStrategy {
private double percentage;
public PercentageDiscount(double percentage) {
this.percentage = percentage;
}
@Override
public double applyDiscount(double originalPrice) {
return originalPrice * (1 - percentage/100);
}
}
// 上下文类
public class PricingContext {
private DiscountStrategy strategy;
public PricingContext(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double calculatePrice(double originalPrice) {
return strategy.applyDiscount(originalPrice);
}
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
PricingContext context = new PricingContext(new NoDiscount());
System.out.println("原价: " + context.calculatePrice(100));
context.setStrategy(new FixedDiscount(20));
System.out.println("固定折扣: " + context.calculatePrice(100));
context.setStrategy(new PercentageDiscount(10));
System.out.println("百分比折扣: " + context.calculatePrice(100));
}
}
对于前端开发者,策略模式同样适用。以下是JavaScript实现:
javascript复制// 策略对象
const strategies = {
"regular": (amount) => amount,
"fixed": (amount) => amount - 10,
"percentage": (amount) => amount * 0.9
};
// 上下文
class ShoppingCart {
constructor(discountStrategy = 'regular') {
this.discountStrategy = discountStrategy;
this.items = [];
}
addItem(item) {
this.items.push(item);
}
calculateTotal() {
const subtotal = this.items.reduce((sum, item) => sum + item.price, 0);
return strategies[this.discountStrategy](subtotal);
}
setDiscountStrategy(strategy) {
this.discountStrategy = strategy;
}
}
// 使用示例
const cart = new ShoppingCart();
cart.addItem({ name: 'Book', price: 50 });
cart.addItem({ name: 'Pen', price: 10 });
console.log('Regular price:', cart.calculateTotal()); // 60
cart.setDiscountStrategy('fixed');
console.log('Fixed discount:', cart.calculateTotal()); // 50
cart.setDiscountStrategy('percentage');
console.log('Percentage discount:', cart.calculateTotal()); // 54
假设我们正在开发一个电商平台,需要支持多种促销活动:
typescript复制interface PromotionStrategy {
applyPromotion(originalPrice: number): number;
}
class FullReduction implements PromotionStrategy {
private threshold: number;
private reduction: number;
constructor(threshold: number, reduction: number) {
this.threshold = threshold;
this.reduction = reduction;
}
applyPromotion(originalPrice: number): number {
return originalPrice >= this.threshold ?
originalPrice - this.reduction :
originalPrice;
}
}
class Discount implements PromotionStrategy {
private discountRate: number;
constructor(discountRate: number) {
this.discountRate = discountRate;
}
applyPromotion(originalPrice: number): number {
return originalPrice * this.discountRate;
}
}
class PromotionContext {
private strategy: PromotionStrategy;
setStrategy(strategy: PromotionStrategy) {
this.strategy = strategy;
}
executePromotion(price: number): number {
return this.strategy.applyPromotion(price);
}
}
// 使用示例
const context = new PromotionContext();
// 满减活动
context.setStrategy(new FullReduction(100, 20));
console.log(context.executePromotion(150)); // 130
// 折扣活动
context.setStrategy(new Discount(0.8));
console.log(context.executePromotion(150)); // 120
策略模式常与工厂模式结合使用,将策略的创建逻辑封装起来:
java复制public class StrategyFactory {
public static DiscountStrategy createStrategy(String strategyType) {
switch(strategyType) {
case "FIXED":
return new FixedDiscount(10);
case "PERCENTAGE":
return new PercentageDiscount(15);
case "SEASONAL":
return new SeasonalDiscount();
default:
return new NoDiscount();
}
}
}
// 使用方式
DiscountStrategy strategy = StrategyFactory.createStrategy("FIXED");
PricingContext context = new PricingContext(strategy);
对于简单的策略,可以使用枚举来实现:
java复制public enum DiscountType {
NONE {
public double apply(double price) { return price; }
},
FIXED_10 {
public double apply(double price) { return price - 10; }
},
PERCENT_20 {
public double apply(double price) { return price * 0.8; }
};
public abstract double apply(double price);
}
// 使用方式
double finalPrice = DiscountType.PERCENT_20.apply(originalPrice);
在Spring应用中,可以利用依赖注入来管理策略:
java复制@Service
public class PaymentService {
private Map<String, PaymentStrategy> strategies;
@Autowired
public PaymentService(List<PaymentStrategy> strategyList) {
strategies = strategyList.stream()
.collect(Collectors.toMap(
PaymentStrategy::getType,
Function.identity()
));
}
public void processPayment(String type, BigDecimal amount) {
PaymentStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new IllegalArgumentException("Unsupported payment type");
}
strategy.process(amount);
}
}
public interface PaymentStrategy {
String getType();
void process(BigDecimal amount);
}
@Service
public class AlipayStrategy implements PaymentStrategy {
@Override
public String getType() {
return "ALIPAY";
}
@Override
public void process(BigDecimal amount) {
// 支付宝支付逻辑
}
}
问题:如何让客户端选择合适的策略?
解决方案:
问题:策略需要访问上下文的数据怎么办?
解决方案:
问题:如何支持多个策略的组合使用?
解决方案:
java复制public class CompositeStrategy implements DiscountStrategy {
private List<DiscountStrategy> strategies;
public CompositeStrategy(List<DiscountStrategy> strategies) {
this.strategies = strategies;
}
@Override
public double applyDiscount(double originalPrice) {
double result = originalPrice;
for (DiscountStrategy strategy : strategies) {
result = strategy.applyDiscount(result);
}
return result;
}
}
策略模式和状态模式在结构上相似,但意图不同:
在实际项目中,我经常发现策略模式被过度使用的情况。对于简单的、不太可能变化的算法差异,有时直接使用条件语句反而更清晰。策略模式真正的价值在于处理那些可能频繁变化、需要灵活组合的复杂算法场景。