当前位置: 首页 > news >正文

告别条件判断:策略模式让代码更优雅

告别条件判断:策略模式让代码更优雅

🎯 策略模式(Strategy Pattern)简介

策略模式 是一种行为型设计模式,定义了一系列算法或行为,并将它们封装起来,使它们可以互换使用。通过将不同的算法封装在独立的策略类中,策略模式使得算法可以独立于使用它的客户端进行变化。

核心思想

策略模式通过封装变化的部分,允许程序在运行时动态选择不同的策略(算法)进行处理,避免了在客户端代码中使用条件语句来选择算法,从而提高了系统的灵活性。

策略模式的 UML 类图

在这里插入图片描述

角色说明

  1. Strategy(策略接口)
    • 定义了一个共同的接口,用于封装算法或行为。所有具体的策略类都会实现这个接口,并定义各自的具体算法。
  2. ConcreteStrategy(具体策略类)
    • 每个具体的策略类实现了 Strategy 接口,定义了一个具体的算法或行为。
  3. Context(上下文类)
    • 上下文类持有一个策略对象,并调用该策略的算法。在运行时,上下文可以更改其使用的策略,以便动态地选择不同的算法。

生动案例:电商平台的促销活动策略

场景说明

假设我们有一个电商平台,在不同的时间段内会有不同的促销活动,比如:

  • 打折促销:给顾客的商品价格打折。
  • 满减促销:当顾客消费满一定金额时,给予一定的减免。

我们希望能灵活地切换不同的促销策略,而不必每次都修改核心代码。可以使用策略模式,根据不同的促销活动动态选择促销算法。

角色映射

  1. Strategy:促销策略接口,定义通用的促销算法。
  2. ConcreteStrategy:具体的促销策略,例如打折、满减等。
  3. 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

策略模式的优缺点

优点

  1. 开闭原则
    • 策略模式使得算法或行为的扩展变得非常容易。你可以通过添加新的策略类来扩展新的行为,而不需要修改已有的代码。
  2. 消除条件判断
    • 通过策略模式,可以避免在客户端代码中使用大量的 if-elseswitch 语句来选择算法,从而使代码更加清晰。
  3. 提高灵活性
    • 在运行时可以根据具体情况动态选择不同的策略,使得系统在不同场景下表现出不同的行为。

缺点

  1. 增加了类的数量
    • 每一个具体的策略都需要实现 Strategy 接口,这会导致类的数量增多,增加了系统的复杂度。
  2. 客户端需要了解策略
    • 客户端必须知道所有的策略类,并且要根据具体情况来选择合适的策略,可能会增加使用的复杂度。

策略模式的应用场景

  1. 算法或行为多变的场景
    • 当一个类中存在多个算法,且这些算法是可以互换的,可以使用策略模式来动态选择算法。
  2. 避免使用条件判断的场景
    • 在需要根据不同条件选择不同的行为时,策略模式可以避免 if-elseswitch 语句的使用。
  3. 需要动态改变行为的场景
    • 当程序需要在运行时根据不同的情况选择不同的行为时,策略模式能够提供灵活的解决方案。

策略思想在优秀框架中的应用

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 是策略接口,AlipayServiceWechatPayService 是具体的策略实现。通过 @Qualifier 注解,Spring 可以动态选择不同的策略进行依赖注入,达到了策略模式的效果。

总结

策略模式 通过将算法或行为抽象为一系列独立的策略类,使得算法可以在客户端代码之外进行动态选择和替换,极大地提高了系统的灵活性和可扩展性。在电商促销活动的案例中,我们通过策略模式展示了如何灵活地切换促销算法,减少代码中的条件判断,使得不同的促销方案可以动态应用。

策略模式非常适合那些算法或行为经常变化的系统,通过将变化的部分封装起来,增强了系统的扩展性和维护性。


http://www.mrgr.cn/news/33626.html

相关文章:

  • Elman 神经网络算法详解
  • Linux故障排查中常用的命令
  • 网络安全-Linux基础(bash脚本)
  • 【软考】系统架构设计师-计算机系统基础(2):操作系统
  • ES6笔记
  • 计算机网络:运输层 —— 运输层端口号
  • c++类与对象一
  • AgentScope中带有@功能的多Agent组对话
  • python爬虫案例——异步加载网站数据抓取,post请求(6)
  • CCF csp认证 小白必看
  • error -- unsupported GNU version gcc later than 10 are not supported;(gcc、g++)
  • 条件编译及头文件包含
  • DAY78服务攻防-数据库安全RedisCouchDBH2database未授权访问CVE 漏洞
  • ModbusTCP通讯错误的排查
  • 数据处理与统计分析篇-day08-apply()自定义函数与分组操作
  • 【掘金量化使用技巧】用日线合成长周期k线
  • golang学习笔记8-运算符与输入
  • 使用Okhttp-服务器不支持缓存的解决办法
  • 百度智能云API调用
  • AI大模型基础概念
  • AD19基础应用技巧:交叉选择/跳转到器件/镜像粘贴/元器件矩形区域排列/选择过滤器/捕捉对象等设置
  • 插件化换肤的优缺点分别是什么
  • 【练习16】求最小公倍数
  • kindle云端同步
  • 项目扩展四:交换机和队列的特性完善【自动删除与队列独占的实现】
  • Java是怎么处理死锁的