当你在开发软件应用程序时,你可能会遇到一些问题,实现你需要的功能。软件设计模式是在使用面向对象设计开发软件应用程序时,对其中一些经常发生的问题的典型解决方案。在这篇文章中,让我们了解一下流行的设计模式之一,策略模式,以及它在Python中的实现。
简介
在深入了解策略模式之前,你应该熟悉面向对象编程(OOP)的一些基本概念。设计模式的整个概念是围绕着类和对象展开的。设计模式是对经常发生的问题的高级解决方案。它们就像解决一个具体问题的蓝图。它们并不局限于单一的编程语言。你可以在任何支持面向对象编程的编程语言中使用设计模式;过程将是相同的,而语法会改变。
有几种类型的设计模式,包括创造型、结构型和行为型。创造性模式是关于创建对象的不同方式,以增加我们代码的灵活性。结构模式是关于对象之间的关系,使用对象和类使更大的结构变得灵活。行为模式是关于对象之间的有效沟通和互动。
策略模式
策略模式是一种设计模式,它使我们的应用程序能够在运行时选择算法,使我们的应用程序变得灵活。GoF编写的关于设计模式的原版书中指出:"策略模式意在定义一个算法系列,封装每一种算法,并使它们可以互换"。更具体地说,它让你定义一组算法,这些算法在运行时可以根据一些因素进行互换。策略模式属于行为设计模式的范畴,因为它使算法的行为在运行时被选择。
使用方法
在开发软件应用程序时,你可能有几个选择来完成你的代码中的一些事情。根据你的客户选择、数据源或其他因素,你想在不改变代码的情况下做一些不同的事情。你常常倾向于在代码的主类中使用条件语句来定义不同情况的算法。但这并不是一种写出更好代码的优雅方式。它使你的代码的主类变得相当长,而且变得太难维护应用程序。
在这样的情况下,策略模式是一个理想的解决方案。策略模式建议你为你的不同情况的算法定义类,称为策略。策略在主类中被引用,称为上下文,代码根据该情况工作。上下文并不为该情况选择合适的策略。相反,客户端会将所需的策略传递给上下文。
例如,如果你有一个国际象棋应用程序,你可以在简单、中等或困难之间选择难度等级。计算机会根据你选择的级别选择一种算法。这是使用策略模式的最佳例子之一。
策略模式遵循开放/封闭原则;一个软件应用程序对扩展是开放的,但对修改是封闭的。这意味着你可以在不修改主类的情况下添加任何数量的额外策略。它使你的代码更加灵活,易于维护。
UML图
下面是策略模式的UML图。

- Context- 它是我们应用程序的主类。它维护对一个具体策略的引用。
- 策略- 策略接口对所有支持的策略来说是通用的。Context只能通过策略接口与其他策略通信。
- ConcreteStrategies- 这些是使用策略接口实现算法的类。
实施
让我们看看实现策略模式的分步过程。
- 你应该首先确定你想在主类中作为具体策略执行的算法。
- 定义上下文(主类),并添加一个对策略的引用,一个设置策略的方法,以及另一个执行策略的方法。你也可以定义一个默认策略,只有在不喜欢默认策略的情况下才切换策略。
## context - the primary class
class Context:
strategy: Strategy ## the strategy interface
def setStrategy(self, strategy: Strategy = None) -> None:
if strategy is not None:
self.strategy = strategy
else:
self.strategy = Default()
def executeStrategy(self) -> str:
print(self.strategy.execute())
首先,我们定义了strategy 字段,用于存储对策略对象的引用,以及两个方法,setStrategy 和executeStrategy 。setStrategy ,如果用户选择了一个选项,则设置所选择的策略,否则就是 default一个。
- 定义策略接口,这是所有具体策略所共有的。
Strategy接口有一个抽象的方法,你可以在具体策略中改变这个方法。
from abc import ABC, abstractmethod
## Strategy interface
class Strategy(ABC):
@abstractmethod
def execute(self) -> str:
pass
- 定义具体策略,这些策略应该实现
Strategy接口。这些具体策略必须有一个共同的方法,该方法重写了Strategy接口的execute方法。
## Concrete strategies
class ConcreteStrategyA(Strategy):
def execute(self) -> str:
return "ConcreteStrategy A"
class ConcreteStrategyB(Strategy):
def execute(self) -> str:
return "ConcreteStrategy B"
class Default(Strategy):
def execute(self) -> str:
return "Default"
- 现在,用户可以在运行时选择他们想要的策略。创建一个上下文的对象,并传递一个具体的策略。
## Example application
appA = Context()
appB = Context()
appC = Context()
## selecting stratigies
appA.setStrategy(ConcreteStrategyA())
appB.setStrategy(ConcreteStrategyB())
appC.setStrategy() ## sets to default strategy
## each object below execute different strategy with same method
appA.executeStrategy()
appB.executeStrategy()
appC.executeStrategy()
上述代码的输出将如下。
ConcreteStrategy A
ConcreteStrategy B
Default
如果你想使用另一个策略,用你想要的策略替换ConcreteStrategy实例。你可以添加一个新的具体策略,而无需改变上下文中的任何内容。
例子
让我们使用策略模式设计一个剪刀石头布的游戏。你可以在石头、布、剪子和随机中选择任何一种策略来与电脑对战。下面的示例代码使用策略模式来实现各种策略。
## Changing the strategy among Rock, Paper, Scissors, and Random
import random
from abc import ABC, abstractmethod
## Strategy interface
class Strategy(ABC):
@abstractmethod
def selection(self) -> None:
pass
## Concrete strategies
class Rock(Strategy):
## actual application will have the algorithm instead this method
def selection(self) -> str:
return "Rock"
class Paper(Strategy):
def selection(self) -> str:
return "Paper"
class Scissors(Strategy):
def selection(self) -> str:
return "Scissors"
class Random(Strategy):
def selection(self) -> str:
options = ["Rock", "Paper", "Scissors"]
return random.choice(options)
## Context class
class Game:
strategy: Strategy
def __init__(self, strategy: Strategy = None) -> None:
if strategy is not None:
self.strategy = strategy
else:
self.strategy = Random()
def play(self, sec) -> None:
s1 = self.strategy.selection()
s2 = sec.strategy.selection()
if s1 == s2:
print("It's a tie")
elif s1 == "Rock":
if s2 == "Scissors":
print("Player 1 wins!")
else:
print("Player 2 wins!")
elif s1 == "Scissors":
if s2 == "Paper":
print("Player 1 wins!")
else:
print("Player 2 wins!")
elif s1 == "Paper":
if s2 == "Rock":
print("Player 1 wins!")
else:
print("Player 2 wins!")
## Example application
## PLayer 1 can select his strategy
player1 = Game(Paper())
# Player 2 gets to select
player2 = Game(Rock())
# After the second player choice, we call the play method
player1.play(player2)
根据两个玩家选择的策略,预期的输出将是。
Player 1 wins!
使用所有其他策略测试游戏的所有其他情况。为了给游戏增加额外的乐趣,可以尝试根据Lizard-Spock的扩展,在上面的例子中再创建两个策略。
总结
在这篇文章中,你已经看到了在哪里以及如何在你的代码中使用策略模式。你可以使用策略模式构建灵活的、可维护的软件应用程序。你可以在运行时根据用户的决定在不同的算法之间进行切换,而无需改变代码。但如果你的代码中只有几个算法,就没有必要使用策略。它只会让你的代码看起来很复杂,有许多类和对象。策略模式可以作为条件语句的替代品,用于选择应用程序的行为。但策略模式的潜在缺点是,用户必须知道策略之间有什么不同,才能选择他们需要的东西。因此,只有当应用程序的行为变化与用户有关时,你才最好使用策略模式。因此,请尝试使用策略模式使你的软件应用灵活。