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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 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. 需要提供多种算法变体: 如果一个类的算法有多个变体,并且这些变体经常需要切换,策略模式是一个不错的选择。

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

目录
相关文章
|
1月前
|
设计模式 算法 Java
Java一分钟之-设计模式:策略模式与模板方法
【5月更文挑战第17天】本文介绍了策略模式和模板方法模式,两种行为设计模式用于处理算法变化和代码复用。策略模式封装不同算法,允许客户独立于具体策略进行选择,但需注意选择复杂度和过度设计。模板方法模式定义算法骨架,延迟部分步骤给子类实现,但过度抽象或滥用继承可能导致问题。代码示例展示了两种模式的应用。根据场景选择合适模式,以保持代码清晰和可维护。
25 1
|
11天前
|
设计模式 算法 架构师
设计模式-策略模式详解
系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
340 0
|
1月前
|
设计模式 算法
【设计模式】阿里终面:你觉得这个例子是策略模式吗?
【设计模式】阿里终面:你觉得这个例子是策略模式吗?
21 1
|
1月前
|
设计模式
【设计模式】张一鸣笔记:责任链接模式怎么用?
【设计模式】张一鸣笔记:责任链接模式怎么用?
25 1
|
1月前
|
设计模式 算法
大话设计模式(2)——策略模式
大话设计模式(2)——策略模式
22 1
|
1月前
|
设计模式 JavaScript 算法
js设计模式-策略模式与代理模式的应用
策略模式和代理模式是JavaScript常用设计模式。策略模式通过封装一系列算法,使它们可互换,让算法独立于客户端,提供灵活的选择。例如,定义不同计算策略并用Context类执行。代理模式则为对象提供代理以控制访问,常用于延迟加载或权限控制。如创建RealSubject和Proxy类,Proxy在调用RealSubject方法前可执行额外操作。这两种模式在复杂业务逻辑中发挥重要作用,根据需求选择合适模式解决问题。
|
1月前
|
设计模式 算法 Java
【设计模式系列笔记】设计模式与设计原则
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 设计原则是一些通用的设计指导方针,它们提供了如何设计一个优秀的软件系统的基本思想和规则。指导着设计者如何组织代码以实现高内聚、低耦合、易扩展和易维护的软件系统。
39 4
|
1月前
|
设计模式 算法 Java
Java 设计模式:探索策略模式的概念和实战应用
【4月更文挑战第27天】策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在 Java 中,策略模式通过定义一系列的算法,并将每一个算法封装起来,并使它们可以互换,这样算法的变化不会影响到使用算法的客户。
31 1
|
1月前
|
设计模式 JavaScript 前端开发
vue的设计模式_笔记
vue的设计模式_笔记
19 0
|
1月前
|
设计模式 算法 Go
[设计模式 Go实现] 行为型~策略模式
[设计模式 Go实现] 行为型~策略模式