1. 策略模式是什么?为什么我们需要它?
我刚入行Java开发时,第一次听说"策略模式"这个术语,脑子里立刻浮现出军事战略的画面。但实际上,策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。这种模式让算法的变化独立于使用算法的客户。
想象你正在开发一个电商平台的支付系统。最初可能只有支付宝支付,代码可能是这样的:
java复制public class PaymentService {
public void pay(String orderId, BigDecimal amount) {
// 调用支付宝SDK
AlipayClient alipayClient = new DefaultAlipayClient(...);
alipayClient.pay(orderId, amount);
}
}
但很快产品经理要求增加微信支付,于是代码变成了:
java复制public class PaymentService {
public void pay(String orderId, BigDecimal amount, String payType) {
if ("alipay".equals(payType)) {
// 支付宝支付逻辑
} else if ("wechat".equals(payType)) {
// 微信支付逻辑
}
}
}
当需要增加银联支付时,又得修改这个类。这种设计明显违反了开闭原则(OCP) - 对扩展开放,对修改关闭。策略模式正是为了解决这类问题而生的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 策略模式的三大核心组件
2.1 策略接口(Strategy Interface)
这是所有具体策略的通用接口,声明了策略类必须实现的方法。在我们的支付例子中,可以这样定义:
java复制public interface PaymentStrategy {
void pay(String orderId, BigDecimal amount);
}
2.2 具体策略类(Concrete Strategies)
这些类实现了策略接口,提供了算法的不同实现:
java复制public class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(String orderId, BigDecimal amount) {
// 具体的支付宝支付实现
System.out.println("使用支付宝支付订单:" + orderId + ",金额:" + amount);
}
}
public class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(String orderId, BigDecimal amount) {
// 具体的微信支付实现
System.out.println("使用微信支付订单:" + orderId + ",金额:" + amount);
}
}
2.3 上下文类(Context)
上下文类持有一个策略对象的引用,并能通过setter方法动态切换策略:
java复制public class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(String orderId, BigDecimal amount) {
strategy.pay(orderId, amount);
}
}
3. 从零实现一个完整的策略模式案例
让我们通过一个更完整的例子来理解策略模式的实际应用。假设我们正在开发一个电商平台的折扣系统,不同类型的用户享受不同的折扣策略。
3.1 定义折扣策略接口
java复制public interface DiscountStrategy {
BigDecimal applyDiscount(BigDecimal originalPrice);
}
3.2 实现具体折扣策略
java复制// 普通会员无折扣
public class RegularMemberStrategy implements DiscountStrategy {
@Override
public BigDecimal applyDiscount(BigDecimal originalPrice) {
return originalPrice;
}
}
// 银牌会员9折
public class SilverMemberStrategy implements DiscountStrategy {
@Override
public BigDecimal applyDiscount(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.9"));
}
}
// 金牌会员8折
public class GoldMemberStrategy implements DiscountStrategy {
@Override
public BigDecimal applyDiscount(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.8"));
}
}
// 钻石会员7折
public class DiamondMemberStrategy implements DiscountStrategy {
@Override
public BigDecimal applyDiscount(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.7"));
}
}
3.3 创建上下文类
java复制public class DiscountContext {
private DiscountStrategy strategy;
public DiscountContext(DiscountStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public BigDecimal calculatePrice(BigDecimal originalPrice) {
return strategy.applyDiscount(originalPrice);
}
}
3.4 客户端使用示例
java复制public class Client {
public static void main(String[] args) {
// 创建一个订单,原价100元
BigDecimal originalPrice = new BigDecimal("100");
// 根据用户等级选择策略
String memberLevel = "gold"; // 实际中可能从数据库或Session获取
DiscountStrategy strategy;
switch (memberLevel) {
case "silver":
strategy = new SilverMemberStrategy();
break;
case "gold":
strategy = new GoldMemberStrategy();
break;
case "diamond":
strategy = new DiamondMemberStrategy();
break;
default:
strategy = new RegularMemberStrategy();
}
DiscountContext context = new DiscountContext(strategy);
BigDecimal finalPrice = context.calculatePrice(originalPrice);
System.out.println("原价: " + originalPrice + ",折后价: " + finalPrice);
}
}
4. 策略模式的进阶应用技巧
4.1 结合工厂模式简化策略创建
当策略类较多时,可以使用工厂模式来创建策略对象:
java复制public class DiscountStrategyFactory {
public static DiscountStrategy getStrategy(String memberLevel) {
switch (memberLevel) {
case "silver": return new SilverMemberStrategy();
case "gold": return new GoldMemberStrategy();
case "diamond": return new DiamondMemberStrategy();
default: return new RegularMemberStrategy();
}
}
}
// 客户端代码简化为
DiscountStrategy strategy = DiscountStrategyFactory.getStrategy(memberLevel);
DiscountContext context = new DiscountContext(strategy);
4.2 使用枚举实现策略模式
对于简单的策略,可以使用枚举来实现:
java复制public enum DiscountStrategyEnum {
REGULAR {
@Override
public BigDecimal apply(BigDecimal price) {
return price;
}
},
SILVER {
@Override
public BigDecimal apply(BigDecimal price) {
return price.multiply(new BigDecimal("0.9"));
}
},
GOLD {
@Override
public BigDecimal apply(BigDecimal price) {
return price.multiply(new BigDecimal("0.8"));
}
},
DIAMOND {
@Override
public BigDecimal apply(BigDecimal price) {
return price.multiply(new BigDecimal("0.7"));
}
};
public abstract BigDecimal apply(BigDecimal price);
}
// 使用方式
BigDecimal finalPrice = DiscountStrategyEnum.valueOf(memberLevel.toUpperCase())
.apply(originalPrice);
4.3 使用Lambda表达式简化策略模式
Java 8以后,如果策略接口只有一个方法,可以直接使用Lambda表达式:
java复制public class DiscountContext {
private Function<BigDecimal, BigDecimal> discountStrategy;
public DiscountContext(Function<BigDecimal, BigDecimal> discountStrategy) {
this.discountStrategy = discountStrategy;
}
public BigDecimal calculatePrice(BigDecimal originalPrice) {
return discountStrategy.apply(originalPrice);
}
}
// 客户端使用
Function<BigDecimal, BigDecimal> goldStrategy = price -> price.multiply(new BigDecimal("0.8"));
DiscountContext context = new DiscountContext(goldStrategy);
BigDecimal finalPrice = context.calculatePrice(originalPrice);
5. 策略模式在Spring框架中的实际应用
在Spring项目中,策略模式可以很好地与依赖注入结合使用。以下是几种常见方式:
5.1 使用@Service注解实现策略
java复制public interface ShippingStrategy {
BigDecimal calculateFee(BigDecimal weight);
}
@Service("standardShipping")
public class StandardShippingStrategy implements ShippingStrategy {
@Override
public BigDecimal calculateFee(BigDecimal weight) {
return weight.multiply(new BigDecimal("5"));
}
}
@Service("expressShipping")
public class ExpressShippingStrategy implements ShippingStrategy {
@Override
public BigDecimal calculateFee(BigDecimal weight) {
return weight.multiply(new BigDecimal("10")).add(new BigDecimal("20"));
}
}
@Service
public class ShippingService {
@Autowired
private Map<String, ShippingStrategy> strategies;
public BigDecimal calculateShippingFee(String strategyName, BigDecimal weight) {
ShippingStrategy strategy = strategies.get(strategyName);
if (strategy == null) {
throw new IllegalArgumentException("未知的配送策略: " + strategyName);
}
return strategy.calculateFee(weight);
}
}
5.2 使用ApplicationContext获取策略Bean
java复制@Service
public class ShippingService {
@Autowired
private ApplicationContext applicationContext;
public BigDecimal calculateShippingFee(String strategyName, BigDecimal weight) {
ShippingStrategy strategy = applicationContext.getBean(strategyName, ShippingStrategy.class);
return strategy.calculateFee(weight);
}
}
5.3 策略模式与Spring的@Qualifier结合
java复制public interface NotificationStrategy {
void sendNotification(String message, String recipient);
}
@Service
@Qualifier("emailNotification")
public class EmailNotificationStrategy implements NotificationStrategy {
@Override
public void sendNotification(String message, String recipient) {
// 发送邮件逻辑
}
}
@Service
@Qualifier("smsNotification")
public class SmsNotificationStrategy implements NotificationStrategy {
@Override
public void sendNotification(String message, String recipient) {
// 发送短信逻辑
}
}
@Service
public class NotificationService {
private final NotificationStrategy strategy;
@Autowired
public NotificationService(@Qualifier("emailNotification") NotificationStrategy strategy) {
this.strategy = strategy;
}
// 或者通过setter方法动态切换
public void setStrategy(NotificationStrategy strategy) {
this.strategy = strategy;
}
public void notifyUser(String message, String recipient) {
strategy.sendNotification(message, recipient);
}
}
6. 策略模式的优缺点与适用场景
6.1 策略模式的主要优点
- 开闭原则:无需修改上下文就能引入新策略
- 避免条件语句:替代大量的if-else或switch-case
- 算法复用:可以在系统的不同部分复用相同的策略
- 运行时切换:可以在运行时切换对象使用的算法
6.2 策略模式的潜在缺点
- 客户端必须了解策略差异:客户端需要知道不同策略的区别以选择合适的策略
- 增加对象数量:每个策略都是一个类,可能增加系统中对象的数量
- 通信开销:策略和上下文之间可能需要交换数据,增加了通信开销
6.3 策略模式的典型应用场景
- 多种算法变体:当系统需要在多种算法中选择一种时
- 消除条件语句:当有许多条件语句围绕同一算法的不同变体时
- 算法需要封装:当算法的实现细节不应该暴露给其他类时
- 运行时决策:当需要在运行时决定使用哪种算法时
7. 策略模式与其他设计模式的关系
7.1 策略模式 vs 工厂模式
工厂模式关注对象的创建,而策略模式关注对象的行为。两者经常结合使用:
- 工厂负责创建具体的策略对象
- 上下文类使用工厂创建的对象来执行策略
7.2 策略模式 vs 状态模式
两者结构相似,但意图不同:
- 策略模式中,策略的选择通常由客户端决定
- 状态模式中,状态转换通常由状态类自身或上下文类控制
7.3 策略模式 vs 模板方法模式
两者都用于算法的变化:
- 策略模式使用对象组合 - 定义算法族,分别封装,使它们可以互相替换
- 模板方法使用类继承 - 定义算法骨架,将某些步骤延迟到子类实现
8. 策略模式在实际项目中的最佳实践
8.1 策略的注册与发现
对于大量策略类,可以实现策略的自动注册:
java复制public interface ProcessorStrategy {
boolean supports(String type);
void process(String data);
}
@Service
public class ImageProcessor implements ProcessorStrategy {
@Override
public boolean supports(String type) {
return "image".equalsIgnoreCase(type);
}
@Override
public void process(String data) {
// 处理图片
}
}
@Service
public class VideoProcessor implements ProcessorStrategy {
@Override
public boolean supports(String type) {
return "video".equalsIgnoreCase(type);
}
@Override
public void process(String data) {
// 处理视频
}
}
@Service
public class ProcessorContext {
@Autowired
private List<ProcessorStrategy> strategies;
public void process(String type, String data) {
strategies.stream()
.filter(s -> s.supports(type))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("未知类型: " + type))
.process(data);
}
}
8.2 策略的配置化
将策略配置化,实现更灵活的策略管理:
properties复制# strategies.properties
discount.regular=com.example.strategy.RegularMemberStrategy
discount.silver=com.example.strategy.SilverMemberStrategy
discount.gold=com.example.strategy.GoldMemberStrategy
discount.diamond=com.example.strategy.DiamondMemberStrategy
java复制public class StrategyFactory {
private static final Properties strategies = new Properties();
static {
try (InputStream is = StrategyFactory.class.getResourceAsStream("/strategies.properties")) {
strategies.load(is);
} catch (IOException e) {
throw new RuntimeException("加载策略配置失败", e);
}
}
public static DiscountStrategy getStrategy(String level) {
String className = strategies.getProperty("discount." + level);
if (className == null) {
throw new IllegalArgumentException("未知的策略级别: " + level);
}
try {
return (DiscountStrategy) Class.forName(className).newInstance();
} catch (Exception e) {
throw new RuntimeException("创建策略实例失败", e);
}
}
}
8.3 策略的性能优化
对于频繁使用的策略,可以考虑以下优化:
- 策略缓存:缓存已创建的策略实例
- 策略池:对于有状态的策略,使用对象池管理
- 策略预热:在系统启动时预加载常用策略
java复制public class CachedStrategyFactory {
private static final Map<String, DiscountStrategy> cache = new ConcurrentHashMap<>();
public static DiscountStrategy getStrategy(String level) {
return cache.computeIfAbsent(level, k -> {
// 实际创建策略的逻辑
return StrategyFactory.getStrategy(level);
});
}
}
9. 策略模式的常见误区与避坑指南
9.1 策略类设计过大
误区:将过多逻辑放入单个策略类,导致策略类变得臃肿。
正确做法:
- 保持策略类单一职责
- 如果策略逻辑复杂,可以进一步分解
- 考虑使用组合模式将多个小策略组合成大策略
9.2 策略选择逻辑复杂
误区:策略选择逻辑过于复杂,包含大量条件判断。
正确做法:
- 将策略选择逻辑封装到工厂类中
- 使用Map或查找表简化选择逻辑
- 考虑使用责任链模式处理复杂的策略选择
9.3 忽略策略间的公共逻辑
误区:在每个策略中重复相同的公共逻辑。
正确做法:
- 使用模板方法模式提取公共逻辑
- 创建抽象基类实现公共方法
- 或者使用装饰器模式包装公共逻辑
9.4 策略与上下文过度耦合
误区:策略需要了解太多上下文信息,导致紧密耦合。
正确做法:
- 通过方法参数传递必要数据
- 保持策略接口简洁
- 如果需要大量上下文数据,考虑使用上下文对象封装
10. 策略模式在Java标准库中的应用实例
10.1 Comparator接口
Java集合框架中的Comparator是策略模式的经典应用:
java复制// 策略接口
public interface Comparator<T> {
int compare(T o1, T o2);
}
// 具体策略
public class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.getAge(), p2.getAge());
}
}
// 上下文 - Collections.sort方法
List<Person> people = ...;
Collections.sort(people, new AgeComparator());
10.2 ThreadPoolExecutor的拒绝策略
Java线程池的拒绝策略也是策略模式的实现:
java复制// 策略接口
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
// 具体策略
public class AbortPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException(...);
}
}
public class CallerRunsPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
// 使用示例
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, unit,
workQueue,
new AbortPolicy() // 设置拒绝策略
);
10.3 LayoutManager in Swing
Java Swing中的布局管理器也是策略模式的例子:
java复制// 策略接口
public interface LayoutManager {
void addLayoutComponent(String name, Component comp);
void removeLayoutComponent(Component comp);
Dimension preferredLayoutSize(Container parent);
Dimension minimumLayoutSize(Container parent);
void layoutContainer(Container parent);
}
// 具体策略
public class BorderLayout implements LayoutManager {...}
public class FlowLayout implements LayoutManager {...}
public class GridLayout implements LayoutManager {...}
// 使用示例
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout()); // 设置布局策略
11. 策略模式与单元测试
策略模式天然支持良好的单元测试,因为每个策略都可以独立测试:
11.1 策略接口测试
java复制public interface DiscountStrategyTest<T extends DiscountStrategy> {
T createStrategy();
@Test
default void testApplyDiscount() {
T strategy = createStrategy();
BigDecimal originalPrice = new BigDecimal("100");
BigDecimal discountedPrice = strategy.applyDiscount(originalPrice);
assertNotNull(discountedPrice);
assertTrue(discountedPrice.compareTo(BigDecimal.ZERO) >= 0);
}
}
11.2 具体策略测试
java复制public class GoldMemberStrategyTest implements DiscountStrategyTest<GoldMemberStrategy> {
@Override
public GoldMemberStrategy createStrategy() {
return new GoldMemberStrategy();
}
@Test
public void testGoldDiscount() {
GoldMemberStrategy strategy = createStrategy();
BigDecimal originalPrice = new BigDecimal("100");
BigDecimal expected = new BigDecimal("80");
assertEquals(0, expected.compareTo(strategy.applyDiscount(originalPrice)));
}
}
11.3 上下文类测试
java复制public class DiscountContextTest {
@Test
public void testContextWithDifferentStrategies() {
DiscountContext context = new DiscountContext(new RegularMemberStrategy());
BigDecimal regularPrice = context.calculatePrice(new BigDecimal("100"));
assertEquals(0, new BigDecimal("100").compareTo(regularPrice));
context.setStrategy(new GoldMemberStrategy());
BigDecimal goldPrice = context.calculatePrice(new BigDecimal("100"));
assertEquals(0, new BigDecimal("80").compareTo(goldPrice));
}
}
12. 策略模式在微服务架构中的应用
在微服务架构中,策略模式可以用于处理服务间的不同交互方式:
12.1 服务调用策略
java复制public interface ServiceInvocationStrategy {
<T> T invoke(String serviceName, String operation, Object[] args, Class<T> returnType);
}
// 同步调用策略
@Service
public class SyncInvocationStrategy implements ServiceInvocationStrategy {
@Override
public <T> T invoke(String serviceName, String operation, Object[] args, Class<T> returnType) {
// 实现同步调用逻辑
}
}
// 异步调用策略
@Service
public class AsyncInvocationStrategy implements ServiceInvocationStrategy {
@Override
public <T> T invoke(String serviceName, String operation, Object[] args, Class<T> returnType) {
// 实现异步调用逻辑
}
}
// 使用示例
@Service
public class OrderService {
@Autowired
private ServiceInvocationStrategy invocationStrategy;
public Order getOrder(String orderId) {
return invocationStrategy.invoke("order-service", "getOrderById",
new Object[]{orderId}, Order.class);
}
}
12.2 断路器策略
java复制public interface CircuitBreakerStrategy {
<T> T execute(Supplier<T> supplier);
}
// 简单断路器实现
@Service
public class SimpleCircuitBreaker implements CircuitBreakerStrategy {
private int failureCount = 0;
private long lastFailureTime = 0;
private final int threshold = 3;
private final long timeout = 30000; // 30秒
@Override
public <T> T execute(Supplier<T> supplier) {
if (failureCount >= threshold &&
System.currentTimeMillis() - lastFailureTime < timeout) {
throw new CircuitBreakerOpenException("断路器已打开");
}
try {
T result = supplier.get();
failureCount = 0; // 重置失败计数
return result;
} catch (Exception e) {
failureCount++;
lastFailureTime = System.currentTimeMillis();
throw e;
}
}
}
13. 策略模式与函数式编程的结合
Java 8引入的函数式特性让策略模式实现更加简洁:
13.1 使用函数接口作为策略
java复制public class FileProcessor {
private Function<File, String> processingStrategy;
public void setProcessingStrategy(Function<File, String> strategy) {
this.processingStrategy = strategy;
}
public String processFile(File file) {
return processingStrategy.apply(file);
}
}
// 使用示例
FileProcessor processor = new FileProcessor();
// 设置文本文件处理策略
processor.setProcessingStrategy(file -> {
try {
return new String(Files.readAllBytes(file.toPath()));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
// 设置图片文件处理策略
processor.setProcessingStrategy(file -> {
// 处理图片的逻辑
return "Processed image: " + file.getName();
});
13.2 使用方法引用简化策略
java复制public class TextProcessor {
private UnaryOperator<String> processingStrategy;
public void setProcessingStrategy(UnaryOperator<String> strategy) {
this.processingStrategy = strategy;
}
public String processText(String input) {
return processingStrategy.apply(input);
}
}
// 使用方法引用作为策略
TextProcessor processor = new TextProcessor();
processor.setProcessingStrategy(String::toUpperCase); // 转大写策略
processor.setProcessingStrategy(String::trim); // 去空格策略
processor.setProcessingStrategy(s -> new StringBuilder(s).reverse().toString()); // 反转策略
14. 策略模式在Android开发中的应用
在Android开发中,策略模式也有很多应用场景:
14.1 图片加载策略
java复制public interface ImageLoadingStrategy {
void loadImage(String url, ImageView imageView);
}
// 具体策略
public class PicassoStrategy implements ImageLoadingStrategy {
@Override
public void loadImage(String url, ImageView imageView) {
Picasso.get().load(url).into(imageView);
}
}
public class GlideStrategy implements ImageLoadingStrategy {
@Override
public void loadImage(String url, ImageView imageView) {
Glide.with(imageView.getContext()).load(url).into(imageView);
}
}
// 使用示例
public class ImageLoader {
private ImageLoadingStrategy strategy;
public ImageLoader(ImageLoadingStrategy strategy) {
this.strategy = strategy;
}
public void displayImage(String url, ImageView imageView) {
strategy.loadImage(url, imageView);
}
}
14.2 网络请求策略
java复制public interface NetworkRequestStrategy {
<T> void executeRequest(String url, Class<T> responseType, Callback<T> callback);
}
// Volley实现
public class VolleyRequestStrategy implements NetworkRequestStrategy {
@Override
public <T> void executeRequest(String url, Class<T> responseType, Callback<T> callback) {
// 使用Volley实现网络请求
}
}
// Retrofit实现
public class RetrofitRequestStrategy implements NetworkRequestStrategy {
@Override
public <T> void executeRequest(String url, Class<T> responseType, Callback<T> callback) {
// 使用Retrofit实现网络请求
}
}
// 使用示例
public class ApiClient {
private NetworkRequestStrategy strategy;
public ApiClient(NetworkRequestStrategy strategy) {
this.strategy = strategy;
}
public <T> void get(String url, Class<T> responseType, Callback<T> callback) {
strategy.executeRequest(url, responseType, callback);
}
}
15. 策略模式在游戏开发中的应用
游戏开发中经常使用策略模式来处理不同的游戏角色行为:
15.1 AI行为策略
java复制public interface AIStrategy {
void execute(GameCharacter character, GameWorld world);
}
// 攻击型AI
public class AggressiveAI implements AIStrategy {
@Override
public void execute(GameCharacter character, GameWorld world) {
// 寻找最近的敌人并攻击
GameCharacter enemy = findNearestEnemy(character, world);
if (enemy != null) {
character.attack(enemy);
}
}
}
// 防御型AI
public class DefensiveAI implements AIStrategy {
@Override
public void execute(GameCharacter character, GameWorld world) {
// 寻找安全位置并移动
Position safePosition = findSafePosition(character, world);
character.moveTo(safePosition);
}
}
// 使用示例
public class GameCharacter {
private AIStrategy aiStrategy;
public void setAIStrategy(AIStrategy strategy) {
this.aiStrategy = strategy;
}
public void update(GameWorld world) {
aiStrategy.execute(this, world);
}
}
15.2 伤害计算策略
java复制public interface DamageCalculationStrategy {
int calculateDamage(Attacker attacker, Defender defender);
}
// 物理伤害计算
public class PhysicalDamageStrategy implements DamageCalculationStrategy {
@Override
public int calculateDamage(Attacker attacker, Defender defender) {
return Math.max(1, attacker.getStrength() - defender.getArmor());
}
}
// 魔法伤害计算
public class MagicalDamageStrategy implements DamageCalculationStrategy {
@Override
public int calculateDamage(Attacker attacker, Defender defender) {
return Math.max(1, attacker.getIntelligence() - defender.getMagicResistance());
}
}
// 使用示例
public class Weapon {
private DamageCalculationStrategy damageStrategy;
public Weapon(DamageCalculationStrategy strategy) {
this.damageStrategy = strategy;
}
public int dealDamage(Attacker attacker, Defender defender) {
return damageStrategy.calculateDamage(attacker, defender);
}
}
16. 策略模式与设计原则的关系
策略模式很好地体现了多个面向对象设计原则:
16.1 开闭原则(OCP)
策略模式允许在不修改现有代码的情况下引入新策略。当需要添加新算法时,只需创建新的策略类并实现策略接口,无需修改使用策略的上下文类。
16.2 单一职责原则(SRP)
每个策略类只负责一个具体的算法或行为,将不同的算法分离到不同的类中,使每个类的职责更加单一。
16.3 依赖倒置原则(DIP)
上下文类依赖于抽象的策略接口,而不是具体的策略实现。这使得高层模块不依赖于低层模块,二者都依赖于抽象。
16.4 接口隔离原则(ISP)
策略接口通常只包含一个方法(如execute()或apply()),保持接口精简,避免"胖接口"问题。
16.5 组合优于继承原则
策略模式使用对象组合而不是继承来改变算法或行为,提供了更大的灵活性。
17. 策略模式在数据库访问中的应用
在数据访问层,策略模式可以用于实现不同的数据访问策略:
17.1 数据源选择策略
java复制public interface DataSourceStrategy {
Connection getConnection() throws SQLException;
}
// MySQL数据源
public class MySQLDataSourceStrategy implements DataSourceStrategy {
@Override
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "pass");
}
}
// PostgreSQL数据源
public class PostgresDataSourceStrategy implements DataSourceStrategy {
@Override
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:postgresql://localhost:5432/mydb", "user", "pass");
}
}
// 使用示例
public class UserRepository {
private DataSourceStrategy dataSource;
public UserRepository(DataSourceStrategy dataSource) {
this.dataSource = dataSource;
}
public User findById(int id) throws SQLException {
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?")) {
stmt.setInt(1, id);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return new User(rs.getInt("id"), rs.getString("name"));
}
return null;
}
}
}
17.2 缓存策略
java复制public interface CacheStrategy {
void put(String key, Object value);
Object get(String key);
void remove(String key);
}
// 内存缓存
public class MemoryCacheStrategy implements CacheStrategy {
private final Map<String, Object> cache = new ConcurrentHashMap<>();
@Override
public void put(String key, Object value) {
cache.put(key, value);
}
@Override
public Object get(String key) {
return cache.get(key);
}
@Override
public void remove(String key) {
cache.remove(key);
}
}
// Redis缓存
public class RedisCacheStrategy implements CacheStrategy {
private final Jedis jedis;
public RedisCacheStrategy(String host, int port) {
this.jedis = new Jedis(host, port);
}
@Override
public void put(String key, Object value) {
jedis.set(key.getBytes(), serialize(value));
}
@Override
public Object get(String key) {
return deserialize(jedis.get(key.getBytes()));
}
@Override
public void remove(String key) {
jedis.del(key.getBytes());
}
private byte[] serialize(Object obj) {...}
private Object deserialize(byte[] bytes) {...}
}
// 使用示例
public class CachedUserRepository {
private UserRepository delegate;
private CacheStrategy cache;
public CachedUserRepository(UserRepository delegate, CacheStrategy cache) {
this.delegate = delegate;
this.cache = cache;
}
public User findById(int id) throws SQLException {
String key = "user:" + id;
User user = (User) cache.get(key);
if (user == null) {
user = delegate.findById(id);
if (user != null) {
cache.put(key, user);
}
}
return user;
}
}
18. 策略模式在日志系统中的应用
日志系统可以使用策略模式来实现不同的日志记录方式:
18.1 日志记录策略
java复制public interface LoggingStrategy {
void log(String level, String message, Throwable throwable);
}
// 控制台日志
public class ConsoleLoggingStrategy implements LoggingStrategy {
@Override
public void log(String level, String message, Throwable throwable) {
System.out.printf("[%s] %s%n", level, message);
if (throwable != null) {
throwable.printStackTrace(System.out);
}
}
}
// 文件日志
public class FileLoggingStrategy implements LoggingStrategy {
private final PrintWriter writer;
public FileLoggingStrategy(String filename) throws IOException {
this.writer = new PrintWriter(new FileWriter(filename, true));
}
@Override
public void log(String level, String message, Throwable throwable) {
writer.printf("[%s] %s%n", level, message);
if (throwable != null) {
throwable.printStackTrace(writer);
}
writer.flush();
}
public void close() {
writer.close();
}
}
// 使用示例
public class Logger {
private LoggingStrategy strategy;
public Logger(LoggingStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(LoggingStrategy strategy) {
this.strategy = strategy;
}
public void info(String message) {
strategy.log("INFO", message, null);
}
public void error(String message, Throwable throwable) {
strategy.log("ERROR", message, throwable);
}
}
18.2 日志格式化策略
java复制public interface LogFormatter {
String format(String level, String message, Throwable throwable);
}
// 简单格式化
public class SimpleFormatter implements LogFormatter {
@Override
public String format(String level, String message, Throwable throwable) {
return String.format("[%s] %s", level, message);
}
}
// 详细格式化
public class DetailedFormatter implements LogFormatter {
@Override
public String format(String level, String message, Throwable throwable) {
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String threadName = Thread.currentThread().getName();
String formatted = String.format("[%s][%s][%s] %s", timestamp, threadName, level, message);
if (throwable != null) {
StringWriter sw = new StringWriter();
throwable.printStackTrace(new PrintWriter(sw));
formatted += "\n" + sw.toString();
}
return formatted;
}
}
// 结合格式化策略的日志记录器
public class FormattedLogger {
private LoggingStrategy loggingStrategy;
private LogFormatter formatter;
public FormattedLogger(LoggingStrategy loggingStrategy, LogFormatter formatter) {
this.loggingStrategy = loggingStrategy;
this.formatter = formatter;
}
public void info(String message) {
String formatted = formatter.format("INFO", message, null);
loggingStrategy.log("INFO", formatted, null);
}
public void error(String message, Throwable throwable) {
String formatted = formatter.format("ERROR", message, throwable);
loggingStrategy.log("ERROR", formatted, throwable);
}
}
19. 策略模式在安全认证中的应用
安全认证系统可以使用策略模式来支持多种认证方式:
19.1 认证策略
java复制public interface AuthenticationStrategy {
User authenticate(String username, String credentials) throws AuthenticationException;
}
// 用户名密码认证
public class UsernamePasswordStrategy implements AuthenticationStrategy {
@Override
public User authenticate(String username, String password) throws AuthenticationException {
// 验证用户名密码逻辑
User user = userRepository.findByUsername(username);
if (user != null && passwordEncoder.matches(password, user.getPassword())) {
return user;
}
throw new AuthenticationException("用户名或密码错误");
}
}
// OAuth认证
public class OAuthStrategy implements AuthenticationStrategy {
@Override
public User authenticate(String token, String ignored) throws AuthenticationException {
// 验证OAuth token逻辑
OAuthUser oauthUser = oauthService.validateToken(token);
User user = userRepository.findByOAuthId(oauthUser.getId());
if (user ==
