自学内容网 自学内容网

【再谈设计模式】策略模式 ~ 算法与行为的灵活调度员

本章内容思维导图:

一、引言

        在软件工程,软件开发过程中,我们常常会遇到这样的情况:需要根据不同的条件或者用户输入来执行不同的算法或者操作流程。例如,在一个电商系统中,根据用户的会员等级,可能需要计算不同的折扣;在图形绘制系统中,根据选择的图形类型(圆形、矩形、三角形等)执行不同的绘制算法。如果使用大量的if - else或者switch - case语句来处理这些不同的情况,代码会变得非常臃肿、难以维护和扩展。

这时候,策略模式就可以派上用场了。

二、定义与描述

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

三、抽象背景

        从抽象的角度来看,策略模式旨在将可变的行为从一个类中分离出来,将其封装成独立的策略类。这样一来,原本依赖于具体行为的类就可以依赖于抽象的策略接口,从而在运行时能够轻松切换不同的策略实现,而不需要对依赖该行为的类进行大量修改。

四、适用场景与现实问题解决

  • 多种算法选择场景
    • 在排序算法中,如果有冒泡排序、快速排序、归并排序等多种算法可供选择,就可以使用策略模式。用户可以根据数据的特点(如数据量大小、数据是否基本有序等)在运行时选择合适的排序策略。
  • 业务规则频繁变化场景
    • 以电商系统中的促销策略为例。促销策略可能包括满减、折扣、赠品等多种形式,并且这些策略可能会随着市场情况不断变化。使用策略模式可以方便地添加、修改或删除促销策略,而不会对整个电商系统的订单处理逻辑造成太大的干扰。

五、策略模式在现实生活中的例子

  • 出行方式选择
    • 我们出行时,有多种出行方式可供选择,如步行、骑自行车、乘坐公交车、开车等。每种出行方式都可以看作是一种策略。我们可以根据距离远近、天气情况、时间紧迫性等因素选择不同的出行策略。在程序中,可以定义一个抽象的“出行方式”接口,然后分别实现“步行策略”、“骑自行车策略”、“乘坐公交车策略”和“开车策略”等具体策略类。

六、初衷与问题解决

  • 初衷
    • 策略模式的初衷是为了提高代码的灵活性和可维护性。通过将算法或行为封装成独立的策略类,使得代码的结构更加清晰,不同的策略可以独立开发、测试和维护。
  • 问题解决
    • 它解决了在复杂业务逻辑中,大量条件判断导致的代码可读性差、难以扩展和维护的问题。当需要添加新的策略时,只需要创建一个新的策略类并实现相应的策略接口,而不需要在大量的条件判断语句中进行修改。

七、代码示例

(一)Java示例

// 策略接口
interface Strategy {
    int doOperation(int num1, int num2);
}

// 具体策略类:加法策略
class ConcreteStrategyAdd implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

// 具体策略类:减法策略
class ConcreteStrategySubtract implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}

// 上下文类
class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

使用示例:

public class Main {
    public static void main(String[] args) {
        Context context = new Context(new ConcreteStrategyAdd());
        int result = context.executeStrategy(5, 3);
        System.out.println("使用加法策略结果: " + result);

        context = new Context(new ConcreteStrategySubtract());
        result = context.executeStrategy(5, 3);
        System.out.println("使用减法策略结果: " + result);
    }
}

类图:

  • 首先定义了Strategy接口,声明了doOperation方法,接着定义了实现该接口的两个具体策略类ConcreteStrategyAddConcreteStrategySubtract,它们都重写了doOperation方法。
  • 然后定义了Context类,它包含一个Strategy类型的私有成员变量,并且有构造函数用于传入策略对象以及executeStrategy方法用于执行对应策略的操作。
  • 最后通过--|>表示继承关系(具体策略类继承接口),通过* - "1"表示关联关系(Context类与Strategy存在关联,一个Context对象关联一个Strategy对象)。

时序图:

  • Client(客户端)作为发起操作的角色,首先创建Context实例并传入不同的具体策略类(如ConcreteStrategyAddConcreteStrategySubtract)实例。
  • 然后客户端调用Context类的executeStrategy方法,Context类再根据其持有的具体策略对象引用去调用相应策略类的doOperation方法来执行具体运算,最后将结果返回给客户端。

使用流程图: 

        使用加法策略和减法策略的完整流程步骤,先是创建具体策略类实例,将其传入Context类构建上下文对象,接着准备参数,然后调用相应方法执行运算并最终返回结果。你可以把这段 PlantUML 代码放到支持 PlantUML 的工具(比如在线的 PlantUML 编辑器等)里,查看生成的可视化的流程图,更直观地理解整个操作流程逻辑。

(二)C++示例

// 策略抽象类
class Strategy {
public:
    virtual int doOperation(int num1, int num2) = 0;
};

// 具体策略类:加法策略
class ConcreteStrategyAdd : public Strategy {
public:
    int doOperation(int num1, int num2) override {
        return num1 + num2;
    }
};

// 具体策略类:减法策略
class ConcreteStrategySubtract : public Strategy {
public:
    int doOperation(int num1, int num2) override {
        return num1 - num2;
    }
};

// 上下文类
class Context {
private:
    Strategy* strategy;
public:
    Context(Strategy* strategy) : strategy(strategy) {}
    int executeStrategy(int num1, int num2) {
        return strategy->doOperation(num1, num2);
    }
};

使用示例:

int main() {
    Context* context = new Context(new ConcreteStrategyAdd());
    int result = context->executeStrategy(5, 3);
    std::cout << "使用加法策略结果: " << result << std::endl;

    context = new Context(new ConcreteStrategySubtract());
    result = context->executeStrategy(5, 3);
    std::cout << "使用减法策略结果: " << result << std::endl;
    return 0;
}

(三)Python示例

# 策略抽象类
from abc import ABC, abstractmethod


class Strategy(ABC):
    @abstractmethod
    def doOperation(self, num1, num2):
        pass


# 具体策略类:加法策略
class ConcreteStrategyAdd(Strategy):
    def doOperation(self, num1, num2):
        return num1 + num2


# 具体策略类:减法策略
class ConcreteStrategySubtract(Strategy):
    def doOperation(self, num1, num2):
        return num1 - num2


# 上下文类
class Context:
    def __init__(self, strategy):
        self.strategy = strategy

    def executeStrategy(self, num1, num2):
        return self.strategy.doOperation(num1, num2)

使用示例:

context = Context(ConcreteStrategyAdd())
result = context.executeStrategy(5, 3)
print("使用加法策略结果:", result)

context = Context(ConcreteStrategySubtract())
result = context.executeStrategy(5, 3)
print("使用减法策略结果:", result)

(四)Go示例

// 策略接口
type Strategy interface {
    doOperation(num1, num2 int) int
}

// 具体策略类:加法策略
type ConcreteStrategyAdd struct{}

func (c ConcreteStrategyAdd) doOperation(num1, num2 int) int {
    return num1 + num2
}

// 具体策略类:减法策略
type ConcreteStrategySubtract struct{}

func (c ConcreteStrategySubtract) doOperation(num1, num2 int) int {
    return num1 - num2
}

// 上下文类
type Context struct {
    strategy Strategy
}

func (c *Context) executeStrategy(num1, num2 int) int {
    return c.strategy.doOperation(num1, num2)
}

使用示例:

func main() {
    context := &Context{strategy: ConcreteStrategyAdd{}}
    result := context.executeStrategy(5, 3)
    println("使用加法策略结果:", result)

    context = &Context{strategy: ConcreteStrategySubtract{}}
    result = context.executeStrategy(5, 3)
    println("使用减法策略结果:", result)
}

八、策略模式的优缺点

(一)优点

  • 提高可维护性
    • 由于每个策略都是一个独立的类,所以当需要修改某个策略时,只需要在相应的策略类中进行修改,不会影响到其他代码。
  • 增强代码的灵活性
    • 可以在运行时动态地切换策略,根据不同的需求选择不同的算法或行为。
  • 符合开闭原则
    • 当需要添加新的策略时,只需要创建一个新的策略类并实现策略接口,而不需要修改现有的代码。

(二)缺点

  • 增加类的数量
    • 每一个策略都需要一个单独的类,对于简单的算法,可能会导致类的数量过多,增加了代码的复杂性。
  • 客户端必须了解策略间的区别
    • 客户端需要知道每个策略的具体功能,以便能够正确地选择和使用策略。

九、策略模式的升级版

  • 策略模式与工厂模式结合
    • 可以将策略模式与工厂模式相结合。工厂模式负责创建策略对象,这样可以将策略对象的创建和使用分离得更加彻底。例如,在一个游戏系统中,根据不同的游戏场景创建不同的游戏策略(如攻击策略、防御策略等),可以使用工厂模式来创建这些策略对象,然后在游戏角色中使用这些策略对象。
  • 策略模式与状态模式的融合
    • 在一些情况下,策略模式和状态模式可以融合使用。当一个对象的状态变化会导致其行为发生变化,并且这些行为可以用策略模式来表示时,可以将两者结合。例如,在一个订单处理系统中,订单的状态(未支付、已支付、已发货等)不同,处理订单的策略(如计算价格、安排发货等)也不同。通过将状态模式和策略模式结合,可以更好地处理这种复杂的业务逻辑。


原文地址:https://blog.csdn.net/wnm23/article/details/144783327

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!