【软件设计】常用设计模式--策略模式

软件设计模式(三)

策略模式(Strategy Pattern)

1. 概念

策略模式是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互换。策略模式让算法独立于使用它的客户而变化。

通过策略模式,我们可以将不同的算法封装到独立的类中,并通过客户端在运行时选择具体的策略类。这种模式的优点是提高了代码的灵活性和可扩展性。

2. 模式结构

策略模式通常包含以下几个组成部分:

  • 策略接口(Strategy): 定义算法的公共接口。
  • 具体策略(Concrete Strategy): 实现不同的算法。
  • 上下文类(Context): 负责使用策略,维护对策略对象的引用。

3. UML 类图

lua 复制代码
+------------------+           +----------------+
|   Context        |           |   Strategy      |
|------------------|           |----------------|
| - strategy: IStrategy |<------| + Algorithm()  |
| + SetStrategy()  |           +----------------+
| + ExecuteAlgorithm() |               /\
+------------------+                  /  \
                                     /    \
                      +----------------+ +----------------+
                      |   StrategyA    | |   StrategyB    |
                      +----------------+ +----------------+
                      | + Algorithm()  | | + Algorithm()  |
                      +----------------+ +----------------+

4. 实现方式

C# 示例
步骤1:定义策略接口
csharp 复制代码
public interface IStrategy
{
    void Algorithm();
}
步骤2:实现具体策略类
csharp 复制代码
public class ConcreteStrategyA : IStrategy
{
    public void Algorithm()
    {
        Console.WriteLine("Using Strategy A.");
    }
}

public class ConcreteStrategyB : IStrategy
{
    public void Algorithm()
    {
        Console.WriteLine("Using Strategy B.");
    }
}
步骤3:实现上下文类
csharp 复制代码
public class Context
{
    private IStrategy _strategy;

    public void SetStrategy(IStrategy strategy)
    {
        _strategy = strategy;
    }

    public void ExecuteAlgorithm()
    {
        _strategy?.Algorithm();
    }
}
步骤4:使用策略模式
csharp 复制代码
class Program
{
    static void Main(string[] args)
    {
        Context context = new Context();

        context.SetStrategy(new ConcreteStrategyA());
        context.ExecuteAlgorithm(); // Output: Using Strategy A.

        context.SetStrategy(new ConcreteStrategyB());
        context.ExecuteAlgorithm(); // Output: Using Strategy B.
    }
}
Java 示例
步骤1:定义策略接口
java 复制代码
public interface Strategy {
    void algorithm();
}
步骤2:实现具体策略类
java 复制代码
public class ConcreteStrategyA implements Strategy {
    @Override
    public void algorithm() {
        System.out.println("Using Strategy A.");
    }
}

public class ConcreteStrategyB implements Strategy {
    @Override
    public void algorithm() {
        System.out.println("Using Strategy B.");
    }
}
步骤3:实现上下文类
java 复制代码
public class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeAlgorithm() {
        strategy.algorithm();
    }
}
步骤4:使用策略模式
java 复制代码
public class Main {
    public static void main(String[] args) {
        Context context = new Context();

        context.setStrategy(new ConcreteStrategyA());
        context.executeAlgorithm(); // Output: Using Strategy A.

        context.setStrategy(new ConcreteStrategyB());
        context.executeAlgorithm(); // Output: Using Strategy B.
    }
}

5. 优点

  • 易于扩展: 可以随时添加新的策略类,而不影响现有系统。
  • 代码复用: 将通用行为抽象出来,避免重复代码。
  • 动态决策: 客户端可以根据不同的情况动态选择策略。

6. 缺点

  • 增加对象数目: 如果策略过多,可能会导致类的数量大幅增加。
  • 策略切换复杂: 如果策略之间的切换过于频繁,可能会影响性能。

7. 应用场景

  • 场景1: 支付系统
    在支付系统中,可以根据用户的选择使用不同的支付方式(如信用卡、PayPal、银行转账等)。不同的支付方式可以作为不同的策略实现,用户在支付时动态选择支付方式。
  • 场景2: 算法的多变实现
    对于排序算法、路径规划算法等场景,可以使用策略模式来封装不同的算法实现。比如,客户端可以根据数据集的大小选择使用快速排序、归并排序等不同的策略。
  • 场景3: 日志记录策略
    在某些系统中,可以根据不同的运行环境(开发、生产)动态决定使用不同的日志记录方式,如控制台日志、文件日志、远程日志等。

8. 与其他模式的比较

  • 与状态模式: 策略模式和状态模式都涉及对象之间的切换,但状态模式强调的是状态转移,而策略模式强调的是算法的变化。
  • 与工厂模式: 工厂模式关注的是对象的创建,策略模式则关注算法的变化。

小结

策略模式非常适用于需要根据不同条件动态选择算法或行为的场景。通过封装不同的策略实现,客户端代码可以更加简洁和灵活。

策略模式变体和实际应用场景

1. 变体

策略模式的基本思想虽然简单,但在实际使用中可以根据具体需求进行扩展和变体。以下是几种常见的变体:

变体1: 组合模式与策略模式结合

策略模式通常用于替换算法,而组合模式用于构建层次结构的复杂对象。将这两者结合,可以让策略模式的不同策略实现与组合对象配合,实现更加灵活的行为变化。

示例

如果我们有一个文件压缩系统,使用组合模式来管理文件夹和文件,同时使用策略模式来定义不同的压缩算法(如 ZIP、RAR、7z 等)。每种压缩策略独立实现,文件夹中的文件可以动态选择压缩策略。

变体2: 动态策略切换

通常,策略模式中策略的切换是由客户端代码来决定的。但在某些情况下,策略可以根据系统状态或外部条件自动切换。比如在某些实时系统中,可以根据负载条件动态切换算法。

示例

在网络请求中,可以根据网络延迟自动切换不同的策略,例如切换超时重试策略或者不同的缓存策略,以保证系统的稳定性和性能。

变体3: 缓存策略实例

为了提高性能,我们可以将策略对象缓存起来,而不是每次都重新创建策略实例。比如在大量重复调用的情况下,可以减少不必要的对象创建和销毁。

示例

在一个大规模应用程序中,不同的日志策略(如文件日志、数据库日志等)可以通过缓存实现。每次切换策略时,优先从缓存中获取,而不是每次都重新实例化。

变体4: 策略模式与模板方法模式结合

策略模式和模板方法模式可以结合使用。模板方法定义了算法的基本框架,但将具体步骤的实现延迟到策略类中。这样可以通过不同的策略类来实现框架的细化。

示例

在数据处理系统中,我们可以定义一个模板方法,处理步骤包括数据加载、处理和保存。

不同的策略类可以实现特定的数据处理方法,如文本处理、图像处理等。

2. 实际应用场景

场景1: 不同折扣策略的实现

电子商务平台通常会提供不同的折扣方式,例如满减、打折、会员优惠等。这些不同的折扣方式可以通过策略模式来实现,客户可以动态选择不同的折扣策略。

C#示例
csharp 复制代码
public interface IDiscountStrategy
{
    decimal ApplyDiscount(decimal totalPrice);
}

public class PercentageDiscount : IDiscountStrategy
{
    private decimal _percentage;
    public PercentageDiscount(decimal percentage)
    {
        _percentage = percentage;
    }

    public decimal ApplyDiscount(decimal totalPrice)
    {
        return totalPrice - (totalPrice * _percentage / 100);
    }
}

public class FlatRateDiscount : IDiscountStrategy
{
    private decimal _flatRate;
    public FlatRateDiscount(decimal flatRate)
    {
        _flatRate = flatRate;
    }

    public decimal ApplyDiscount(decimal totalPrice)
    {
        return totalPrice - _flatRate;
    }
}

public class Context
{
    private IDiscountStrategy _strategy;

    public void SetDiscountStrategy(IDiscountStrategy strategy)
    {
        _strategy = strategy;
    }

    public decimal GetDiscountedPrice(decimal totalPrice)
    {
        return _strategy.ApplyDiscount(totalPrice);
    }
}

客户可以根据不同的促销活动选择合适的折扣策略:

csharp 复制代码
var context = new Context();
context.SetDiscountStrategy(new PercentageDiscount(10));
Console.WriteLine(context.GetDiscountedPrice(100)); // 打九折

context.SetDiscountStrategy(new FlatRateDiscount(20));
Console.WriteLine(context.GetDiscountedPrice(100)); // 减去20元
场景2: 动态路由选择

在网络通信或导航系统中,路由算法可能会根据不同的需求动态调整。

例如,有时需要使用最短路径算法,有时需要使用最安全路径算法。策略模式可以用于封装这些不同的路径规划算法。

场景3: 图像渲染策略

在图像处理系统中,可以根据图像的质量或显示设备的性能来动态选择渲染算法。

对于高性能设备,可以选择复杂的高质量渲染算法;对于低性能设备,可以选择简单的低质量渲染策略。

场景4: 动态加密算法选择

在安全系统中,可以根据数据的敏感性或系统资源选择不同的加密算法。

某些场景下,可能需要使用快速但安全性较低的加密算法;而在更敏感的数据场景下,则使用更强的加密策略。

场景5: 数据导入导出

在企业软件中,数据导入导出功能可能支持多种格式(如CSV、XML、JSON等)。

可以使用策略模式封装每种格式的处理逻辑,用户可以根据需求选择不同的导入导出策略。

小结

策略模式的变体和实际应用展示了它的灵活性和扩展性。在复杂的系统中,策略模式可以帮助我们应对多变的需求,并为系统的可维护性提供保障。

相关推荐
思忖小下7 小时前
梳理你的思路(从OOP到架构设计)_简介设计模式
设计模式·架构·eit
liyinuo20179 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范
aaasssdddd9611 小时前
C++的封装(十四):《设计模式》这本书
数据结构·c++·设计模式
T1an-111 小时前
设计模式之【观察者模式】
观察者模式·设计模式
思忖小下13 小时前
梳理你的思路(从OOP到架构设计)_设计模式Factory Method模式
设计模式·工厂方法模式·eit
霁月风14 小时前
设计模式——工厂方法模式
c++·设计模式·工厂方法模式
发飙的蜗牛'16 小时前
23种设计模式
android·java·设计模式
NorthCastle1 天前
设计模式-创建型模式-简单工厂模式详解
设计模式·简单工厂模式
越甲八千1 天前
重拾设计模式-外观模式和适配器模式的异同
设计模式·适配器模式·外观模式
越甲八千1 天前
重拾设计模式--适配器模式
设计模式·适配器模式