告别条件判断:策略模式让代码更优雅
告别条件判断:策略模式让代码更优雅
🎯 策略模式(Strategy Pattern)简介
策略模式 是一种行为型设计模式,定义了一系列算法或行为,并将它们封装起来,使它们可以互换使用。通过将不同的算法封装在独立的策略类中,策略模式使得算法可以独立于使用它的客户端进行变化。
核心思想:
策略模式通过封装变化的部分,允许程序在运行时动态选择不同的策略(算法)进行处理,避免了在客户端代码中使用条件语句来选择算法,从而提高了系统的灵活性。
策略模式的 UML 类图
角色说明:
- Strategy(策略接口):
- 定义了一个共同的接口,用于封装算法或行为。所有具体的策略类都会实现这个接口,并定义各自的具体算法。
- ConcreteStrategy(具体策略类):
- 每个具体的策略类实现了
Strategy
接口,定义了一个具体的算法或行为。
- 每个具体的策略类实现了
- Context(上下文类):
- 上下文类持有一个策略对象,并调用该策略的算法。在运行时,上下文可以更改其使用的策略,以便动态地选择不同的算法。
生动案例:电商平台的促销活动策略
场景说明:
假设我们有一个电商平台,在不同的时间段内会有不同的促销活动,比如:
- 打折促销:给顾客的商品价格打折。
- 满减促销:当顾客消费满一定金额时,给予一定的减免。
我们希望能灵活地切换不同的促销策略,而不必每次都修改核心代码。可以使用策略模式,根据不同的促销活动动态选择促销算法。
角色映射:
- Strategy:促销策略接口,定义通用的促销算法。
- ConcreteStrategy:具体的促销策略,例如打折、满减等。
- Context:购物车,负责根据不同的促销策略计算最终价格。
代码实现:电商促销活动策略
Step 1: 定义策略接口
PromotionStrategy
接口定义了一个通用的促销方法。
// 策略接口:促销策略
public interface PromotionStrategy {double applyDiscount(double price);
}
Step 2: 实现具体的策略类
具体策略类实现 PromotionStrategy
接口,根据不同的促销活动,返回相应的折扣或优惠价格。
打折策略
// 具体策略类:打折促销
public class PercentageDiscountStrategy implements PromotionStrategy {private double discountPercentage;public PercentageDiscountStrategy(double discountPercentage) {this.discountPercentage = discountPercentage;}@Overridepublic double applyDiscount(double price) {return price * (1 - discountPercentage / 100);}
}
满减策略
// 具体策略类:满减促销
public class ThresholdDiscountStrategy implements PromotionStrategy {private double threshold;private double discount;public ThresholdDiscountStrategy(double threshold, double discount) {this.threshold = threshold;this.discount = discount;}@Overridepublic double applyDiscount(double price) {if (price >= threshold) {return price - discount;}return price;}
}
Step 3: 定义上下文类
ShoppingCart
类作为上下文,它通过持有一个 PromotionStrategy
来动态选择促销策略。
// 上下文类:购物车
public class ShoppingCart {private PromotionStrategy strategy;public void setPromotionStrategy(PromotionStrategy strategy) {this.strategy = strategy;}public double calculateTotalPrice(double price) {if (strategy != null) {return strategy.applyDiscount(price);}return price;}
}
Step 4: 测试策略模式
public class StrategyPatternDemo {public static void main(String[] args) {ShoppingCart cart = new ShoppingCart();// 设置打折促销策略cart.setPromotionStrategy(new PercentageDiscountStrategy(10)); // 10% 打折double discountedPrice = cart.calculateTotalPrice(200);System.out.println("Discounted price with 10% off: " + discountedPrice);// 设置满减促销策略cart.setPromotionStrategy(new ThresholdDiscountStrategy(300, 50)); // 满300减50double discountedPrice2 = cart.calculateTotalPrice(350);System.out.println("Discounted price with threshold discount: " + discountedPrice2);}
}
输出结果:
Discounted price with 10% off: 180.0
Discounted price with threshold discount: 300.0
策略模式的优缺点
优点:
- 开闭原则:
- 策略模式使得算法或行为的扩展变得非常容易。你可以通过添加新的策略类来扩展新的行为,而不需要修改已有的代码。
- 消除条件判断:
- 通过策略模式,可以避免在客户端代码中使用大量的
if-else
或switch
语句来选择算法,从而使代码更加清晰。
- 通过策略模式,可以避免在客户端代码中使用大量的
- 提高灵活性:
- 在运行时可以根据具体情况动态选择不同的策略,使得系统在不同场景下表现出不同的行为。
缺点:
- 增加了类的数量:
- 每一个具体的策略都需要实现
Strategy
接口,这会导致类的数量增多,增加了系统的复杂度。
- 每一个具体的策略都需要实现
- 客户端需要了解策略:
- 客户端必须知道所有的策略类,并且要根据具体情况来选择合适的策略,可能会增加使用的复杂度。
策略模式的应用场景
- 算法或行为多变的场景:
- 当一个类中存在多个算法,且这些算法是可以互换的,可以使用策略模式来动态选择算法。
- 避免使用条件判断的场景:
- 在需要根据不同条件选择不同的行为时,策略模式可以避免
if-else
或switch
语句的使用。
- 在需要根据不同条件选择不同的行为时,策略模式可以避免
- 需要动态改变行为的场景:
- 当程序需要在运行时根据不同的情况选择不同的行为时,策略模式能够提供灵活的解决方案。
策略思想在优秀框架中的应用
JDK 中的 Comparator
接口
Comparator
是 Java 集合框架中策略模式的一个经典应用。它允许我们定义对象排序的策略,并可以在不同场景中使用不同的比较策略。
如何应用策略模式:
Comparator
接口定义了比较两个对象的方法compare()
。- 我们可以创建多个
Comparator
实现类,每个类对应不同的排序策略(如按名称、年龄排序等)。 Collections.sort()
方法可以动态地使用不同的Comparator
来实现不同的排序逻辑。
示例代码:
import java.util.*;public class ComparatorStrategyExample {public static void main(String[] args) {List<Person> people = new ArrayList<>();people.add(new Person("Alice", 30));people.add(new Person("Bob", 25));people.add(new Person("Charlie", 35));// 按照姓名排序Collections.sort(people, new NameComparator());System.out.println("Sorted by name: " + people);// 按照年龄排序Collections.sort(people, new AgeComparator());System.out.println("Sorted by age: " + people);}
}class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return name + " (" + age + ")";}
}// 按照姓名排序的策略
class NameComparator implements Comparator<Person> {@Overridepublic int compare(Person p1, Person p2) {return p1.name.compareTo(p2.name);}
}// 按照年龄排序的策略
class AgeComparator implements Comparator<Person> {@Overridepublic int compare(Person p1, Person p2) {return Integer.compare(p1.age, p2.age);}
}
Comparator
是策略模式的典型实现,不同的 Comparator
类实现了不同的排序策略(按姓名或按年龄排序)。Collections.sort()
可以根据传入的 Comparator
动态选择排序策略。
Java 的 ThreadPoolExecutor
在 Java 的并发包中,ThreadPoolExecutor
也是策略模式的一个重要应用。ThreadPoolExecutor
允许你为线程池配置不同的任务提交、拒绝和管理策略。
如何应用策略模式:
RejectedExecutionHandler
接口定义了任务提交失败时的策略。- 我们可以通过实现
RejectedExecutionHandler
接口来创建不同的任务拒绝策略(如丢弃任务、抛异常等)。 ThreadPoolExecutor
动态使用这些策略处理任务提交失败的情况
import java.util.concurrent.*;public class ThreadPoolStrategyExample {public static void main(String[] args) {// 创建一个线程池,并设置拒绝策略ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(2),new RejectedTaskHandler()); // 使用自定义拒绝策略// 提交超出容量的任务for (int i = 0; i < 5; i++) {executor.submit(new Task("Task " + i));}executor.shutdown();}
}// 自定义任务
class Task implements Runnable {private String name;public Task(String name) {this.name = name;}@Overridepublic void run() {System.out.println("Executing " + name);}
}// 自定义拒绝策略
class RejectedTaskHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("Task rejected: " + ((Task) r).name);}
}
Spring 中的 @Qualifier
和 @Primary
(依赖注入策略)
在 Spring 框架中,@Qualifier
和 @Primary
注解用于指定依赖注入时的策略选择。这也可以看作是一种策略模式的应用,允许 Spring 选择适合的实现进行注入。
如何应用策略模式:
- 当多个实现类实现同一个接口时,Spring 允许通过
@Qualifier
或@Primary
来指定注入的策略,即选择注入哪个具体的实现类。 - 这种注解配置相当于动态选择了一个依赖注入策略。
示例代码:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.context.annotation.Primary;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;// 通用接口
interface PaymentService {void pay();
}// 具体策略类1:支付宝支付
@Component
@Qualifier("alipay")
class AlipayService implements PaymentService {@Overridepublic void pay() {System.out.println("Paying with Alipay.");}
}// 具体策略类2:微信支付
@Component
@Primary // 这个是默认注入策略
class WechatPayService implements PaymentService {@Overridepublic void pay() {System.out.println("Paying with Wechat Pay.");}
}// 上下文:支付服务使用
@Component
class PaymentProcessor {private final PaymentService paymentService;public PaymentProcessor(@Qualifier("alipay") PaymentService paymentService) {this.paymentService = paymentService;}public void processPayment() {paymentService.pay();}
}// Spring配置与测试
@Component
public class StrategySpringDemo {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(StrategySpringDemo.class);PaymentProcessor processor = context.getBean(PaymentProcessor.class);processor.processPayment(); // 使用支付宝策略进行支付}
}
输出结果:
Paying with Alipay.
PaymentService
是策略接口,AlipayService
和 WechatPayService
是具体的策略实现。通过 @Qualifier
注解,Spring 可以动态选择不同的策略进行依赖注入,达到了策略模式的效果。
总结
策略模式 通过将算法或行为抽象为一系列独立的策略类,使得算法可以在客户端代码之外进行动态选择和替换,极大地提高了系统的灵活性和可扩展性。在电商促销活动的案例中,我们通过策略模式展示了如何灵活地切换促销算法,减少代码中的条件判断,使得不同的促销方案可以动态应用。
策略模式非常适合那些算法或行为经常变化的系统,通过将变化的部分封装起来,增强了系统的扩展性和维护性。