【设计模式系列笔记】策略模式

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列的算法,将每个算法封装起来,并且使它们可以互相替换。策略模式使得算法可以独立于客户端而变化。

1. 策略模式介绍

策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列的算法,将每个算法封装起来,并且使它们可以互相替换。策略模式使得算法可以独立于客户端而变化。

在Java中,策略模式通常涉及两个主要角色:上下文(Context)和策略(Strategy)。上下文负责维护一个对策略对象的引用,而策略定义了一个接口或抽象类,包含了具体算法的实现。

2. 关键思想

策略模式的关键思想是定义一系列算法,将每个算法封装起来,使它们可以相互替换,而且使得客户端代码独立于具体的算法实现。以下是策略模式的一些关键思想:

  1. 定义一组算法接口或抽象类: 策略模式首先需要定义一个接口或抽象类,它声明了一组算法的方法。这个接口或抽象类通常被称为"策略"。
  2. 实现具体的策略类: 针对上述的接口或抽象类,实现一组具体的策略类,每个类都包含了算法的具体实现。
  3. 在上下文中使用策略: 上下文是使用算法的地方,它包含一个对策略的引用。上下文可以在运行时更改所使用的策略,从而影响其行为。
  4. 使策略可以相互替换: 策略模式的核心是使得不同的算法可以相互替换,而不影响客户端的代码。这通过将策略作为对象传递给上下文来实现。
  5. 封装变化: 策略模式将不同的算法封装在独立的策略类中,从而使得每个算法的变化不会影响到其他算法。这有助于维护和扩展。
  6. 客户端与算法的解耦: 策略模式实现了客户端与具体算法的解耦,使得客户端可以独立于具体算法的变化而变化。

策略模式常用于需要在运行时选择算法的场景,或者当有多个类似的类,它们之间的区别仅在于它们的行为时。通过使用策略模式,可以更灵活地管理和扩展算法,同时保持代码的清晰度和可维护性。

3. 实现方式

策略模式的实现方式通常涉及以下几个步骤:

  1. 定义策略接口或抽象类: 声明一组算法的接口或抽象类,这个接口或抽象类就是策略,其中包含了算法的方法。
  2. 实现具体的策略类: 针对策略接口或抽象类,实现一组具体的策略类,每个类都包含了算法的具体实现。
  3. 创建上下文类: 上下文类负责维护一个对策略对象的引用,并提供一个接口供客户端设置和使用策略。上下文类中通常包含了调用策略的方法。
  4. 客户端使用上下文类: 客户端通过创建上下文类的实例,并通过设置不同的策略对象来实现不同的算法。
// 定义策略接口
interface PaymentStrategy {
    double calculateTotal(double orderAmount);
}
// 具体策略实现 - 支付宝支付
class AlipayPaymentStrategy implements PaymentStrategy {
    @Override
    public double calculateTotal(double orderAmount) {
        // 具体的算法实现
        // 假设支付宝支付有折扣
        return orderAmount * 0.9;
    }
}
// 具体策略实现 - 微信支付
class WeChatPaymentStrategy implements PaymentStrategy {
    @Override
    public double calculateTotal(double orderAmount) {
        // 具体的算法实现
        // 假设微信支付无折扣
        return orderAmount;
    }
}
// 上下文类
class OrderContext {
    private PaymentStrategy paymentStrategy;
    // 设置策略
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    // 根据当前策略计算总价
    public double calculateTotal(double orderAmount) {
        return paymentStrategy.calculateTotal(orderAmount);
    }
}
// 客户端代码
public class StrategyPatternExample {
    public static void main(String[] args) {
        OrderContext orderContext = new OrderContext();
        // 使用支付宝支付策略
        orderContext.setPaymentStrategy(new AlipayPaymentStrategy());
        double totalWithAlipay = orderContext.calculateTotal(100.0);
        System.out.println("支付宝支付: " + totalWithAlipay);
        // 使用微信支付策略
        orderContext.setPaymentStrategy(new WeChatPaymentStrategy());
        double totalWithWeChat = orderContext.calculateTotal(100.0);
        System.out.println("微信支付: " + totalWithWeChat);
    }
}

在上面的例子中,PaymentStrategy是策略接口,AlipayPaymentStrategyWeChatPaymentStrategy是具体的策略实现。OrderContext是上下文类,它持有一个对策略的引用,并通过calculateTotal方法调用具体的策略来计算总价。客户端可以根据需要设置不同的支付策略。

要点:

  1. 定义清晰的策略接口或抽象类: 策略接口或抽象类应该定义算法的方法,确保各个具体策略都实现了相同的接口,以便它们可以互相替换。
  2. 实现具体的策略类: 对于每个算法,创建一个具体的策略类,并在这些类中提供具体的算法实现。
  3. 上下文类的存在: 引入上下文类来维护对策略对象的引用,并在运行时设置所需的具体策略。
  4. 客户端选择策略: 客户端负责选择并设置适当的策略,以满足特定的需求。
  5. 封装变化: 策略模式的目标是封装不同算法的变化,使得这些变化不会影响到客户端的代码。

注意事项:

  1. 选择合适的场景: 策略模式适用于有多个相关算法,并且需要在运行时动态选择其中一个算法的情况。如果系统中只有一个算法,或者算法不经常变化,可能没有必要使用策略模式。
  2. 过度使用: 不要过度使用策略模式,否则可能导致类的数量过多,增加系统的复杂性。只有在确实需要支持多个算法,并且这些算法可以相互替换时,才使用策略模式。
  3. 运行时切换策略: 一个重要的优势是能够在运行时切换策略。确保上下文类的接口和方法能够支持动态切换不同的策略。
  4. 性能考虑: 在一些性能敏感的场景,动态切换策略可能会引入一些开销。在这种情况下,需要仔细评估性能影响。
  5. 与工厂模式结合使用: 有时候,策略模式和工厂模式可以结合使用,通过工厂模式创建具体的策略对象,提供更灵活的对象创建方式。

总的来说,策略模式是一种强大的设计模式,能够使得代码更具有扩展性和灵活性,但在使用时需要权衡设计的复杂性和应用场景的实际需求。

优点:

  1. 灵活性和可扩展性: 策略模式允许在运行时选择算法,提供了灵活性。新的算法可以通过实现新的策略类并注入到上下文中,而不需要修改客户端代码。
  2. 避免使用多重条件语句: 策略模式可以避免使用大量的条件语句来选择不同的算法,使得代码更加清晰、简洁。
  3. 算法独立性: 算法被封装在独立的策略类中,使得这些算法可以独立于上下文类而变化,提高了代码的可维护性和可复用性。
  4. 易于测试: 策略模式使得单元测试变得更容易,因为每个算法都有自己的类,可以独立测试。

缺点:

  1. 类数量增多: 使用策略模式会导致系统中类的数量增加,每个具体策略都需要一个单独的类,可能会使得类的管理和维护变得复杂。
  2. 客户端需要知道所有的策略: 客户端需要了解所有的策略类,并在使用时选择适当的策略,这可能增加了客户端的复杂性。
  3. 上下文对策略的了解: 上下文类需要了解所有的策略,这可能导致上下文类的职责较重,违反了开闭原则。

应用场景:

  1. 算法经常变化: 当一个系统的算法需要经常切换时,使用策略模式可以方便地在运行时选择不同的算法。
  2. 避免使用多重条件语句: 当一个类有很多条件语句来选择不同的行为时,使用策略模式可以提高代码的可读性和可维护性。
  3. 相似行为的不同实现: 当类的相似行为有不同的实现时,可以使用策略模式将这些行为抽象成策略,实现不同的具体策略类。
  4. 需要提供多种算法变体: 如果一个类的算法有多个变体,并且这些变体经常需要切换,策略模式是一个不错的选择。

总的来说,策略模式在需要灵活处理算法的场景中是非常有用的,但在使用时需要根据实际情况权衡其优缺点。

目录
相关文章
|
27天前
|
设计模式 算法 Kotlin
Kotlin - 改良设计模式 - 策略模式
Kotlin - 改良设计模式 - 策略模式
45 4
|
1月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
12天前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
27 2
|
12天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
18 2
|
18天前
|
设计模式 Java Kotlin
Kotlin教程笔记(54) - 改良设计模式 - 迭代器模式
Kotlin教程笔记(54) - 改良设计模式 - 迭代器模式
37 2
|
18天前
|
设计模式 Java API
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
38 2
|
18天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
36 1
|
18天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
36 1
|
21天前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
25 2
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式