策略
范围
对象
目的
行为
意图
定义一系列算法,封装每个算法,并使它们可以互换,以便客户端和算法能够独立地变化。
适用性
- 当一个对象应该使用几种算法之一配置时,
- 并且所有算法都可以封装,
- 并且一个接口涵盖所有封装。
结构
后果
- + 更大的灵活性,重用
- + 可以动态地改变算法
- - 策略创建和通信开销
- - 策略接口不灵活
实现
- 在策略与其上下文之间交换信息
- 通过模板进行静态策略选择
相关模式
- 状态,可以激活多个状态,而策略只能激活一个算法。
- 享元,提供一个共享对象,可以同时在多个上下文中使用,而策略则专注于一个上下文。
- 装饰器,改变对象的皮肤,而策略改变对象的内在机制。
- 组合,与策略结合使用以提高效率。
描述
假设您在一家开发策略游戏的公司工作。让我们假设您已经提出了以下类层次结构。所有角色都能够行走,并且还有一种方法可以在屏幕上渲染它们。超类负责walk()
方法的实现,而每个子类都提供自己的display()
实现,因为每个角色看起来都不一样。
一个新的需求出现了,角色也需要战斗。您可能会说,这很简单,只需在Character
超类中添加一个fight()
方法即可。但是,等等,Worker
怎么办?他们不能战斗!
好吧,您可能会认为,您可以在Worker
子类中简单地覆盖fight()
方法,使其什么也不做。
class Worker {
....
void fight() {
// do nothing
}
....
}
但如果将来需要一个Guard
类,它可以战斗但不能行走?我们需要一个更简洁的解决方案。如何将战斗和行走行为从超类中提取到接口中?这样,只有应该行走的角色才会实现Walkable
接口,而只有应该战斗的角色才会实现Fightable
接口。
好吧,这是另一种说法,即重复代码。如果需要对战斗行为进行少量更改,您需要修改所有类。此时,我们应该制定三个设计原则,以遵循我们的应用程序开发。
- 识别应用程序中变化的部分,并将它们与保持不变的部分分离。将变化的部分“封装”起来,使其不会影响代码的其余部分。
- 面向接口编程,而不是面向实现编程。
- 优先选择组合而不是继承。
所以,让我们应用第一个设计原则。将战斗和行走行为提取到不同的类中。为了应用第二个设计原则,我们必须将这些行为提取到接口中。因此,我们创建一个新的WeaponBehavior
接口来处理战斗行为,类似地,创建一个WalkBehavior
接口来处理行走行为。
Character
的行为存在于单独的类中,这些类实现特定的行为接口。这样,Character
类就不需要知道其自身行为的任何实现细节。此外,我们不再依赖于实现,而是依赖于接口。其他类型的对象也可以使用这些行为,因为它们没有隐藏在我们的Character
类中。我们可以在不修改任何现有行为或触碰我们的角色类的情况下添加新行为。所以现在,我们所要做的就是让我们的Character
类将所有行为信息委托给这两个行为接口(因此,这里出现了第三个设计原则)。我们通过在Character
中添加两个实例变量来实现这一点:weapon
和walk
。
public abstract class Character {
private WeaponBehavior weapon;
private WalkBehavior walk;
public void fight() {
weapon.useWeapon(); // delegation of fight behavior
}
public void setWeapon(WeaponBehavior w) {
weapon = w;
}
...
abstract void display();
}
每个角色对象将在运行时多态地设置这些变量,以引用它想要的特定行为类型。
public class Knight extends Character {
public Knight() {
weapon = new SwordBehavior();
walk = new GallopBehavior();
}
public void display() {
...
}
}
将这些行为视为算法族。因此,组合为您提供了很大的灵活性。它不仅允许您将一系列算法封装到它们自己的类集中,而且还允许您在运行时更改行为,只要您正在组合的对象实现了正确的行为接口。
示例
成本
在实现此模式之前三思而后行。您必须确保您的需求是频繁更改算法。您必须清楚地预见未来,否则,此模式将比基本实现更昂贵。
创建
此模式的创建成本很高。
维护
此模式的维护成本可能很高。如果类的表示形式经常发生变化,您将需要进行大量重构。
删除
此模式也很难删除。
建议
- 使用策略一词向其他开发人员表明使用该模式。
实现
在ActionScript 3 中的策略模式
//invoked from application.initialize
private function init() : void
{
var context:Context;
context = new Context( new ConcreteStrategyA() );
context.execute();
context = new Context( new ConcreteStrategyB() );
context.execute();
context = new Context( new ConcreteStrategyC() );
context.execute();
}
package org.wikipedia.patterns.strategy
{
public interface IStrategy
{
function execute() : void ;
}
}
package org.wikipedia.patterns.strategy
{
public final class ConcreteStrategyA implements IStrategy
{
public function execute():void
{
trace( "ConcreteStrategyA.execute(); invoked" );
}
}
}
package org.wikipedia.patterns.strategy
{
public final class ConcreteStrategyB implements IStrategy
{
public function execute():void
{
trace( "ConcreteStrategyB.execute(); invoked" );
}
}
}
package org.wikipedia.patterns.strategy
{
public final class ConcreteStrategyC implements IStrategy
{
public function execute():void
{
trace( "ConcreteStrategyC.execute(); invoked" );
}
}
}
package org.wikipedia.patterns.strategy
{
public class Context
{
private var strategy:IStrategy;
public function Context(strategy:IStrategy)
{
this.strategy = strategy;
}
public function execute() : void
{
strategy.execute();
}
}
}
在C 中,可以使用结构体来定义类,并使用函数指针来设置策略。以下是 Python 示例的镜像,并使用 C99 特性
#include <stdio.h>
void print_sum(int n, int *array) {
int total = 0;
for (int i = 0; i < n; i++)
total += array[i];
printf("%d", total);
}
void print_array(int n, int *array) {
for (int i = 0; i < n; i++)
printf("%d ", array[i]);
}
typedef struct {
void (*submit_func)(int n, int *array); // function pointer
char *label; // instance label
} Button;
int main(void) {
// Create two instances with different strategies
Button button1 = { print_sum, "Add 'em" };
Button button2 = { print_array, "List 'em" };
int n = 10;
int numbers[n];
for (int i = 0; i < n; i++)
numbers[i] = i;
button1.submit_func(n, numbers);
button2.submit_func(n, numbers);
return 0;
}
在C++ 中的策略模式类似于 Java,但不需要动态分配对象。
#include <iostream>
class Strategy
{
public:
virtual int execute (int a, int b) = 0; // execute() is a so-called pure virtual function
// as a consequence, Strategy is a so-called abstract class
};
class ConcreteStrategyAdd:public Strategy
{
public:
int execute(int a, int b)
{
std::cout << "Called ConcreteStrategyAdd's execute()\n";
return a + b;
}
};
class ConcreteStrategySubstract:public Strategy
{
public:
int execute(int a, int b)
{
std::cout << "Called ConcreteStrategySubstract's execute()\n";
return a - b;
}
};
class ConcreteStrategyMultiply:public Strategy
{
public:
int execute(int a, int b)
{
std::cout << "Called ConcreteStrategyMultiply's execute()\n";
return a * b;
}
};
class Context
{
private:
Strategy* pStrategy;
public:
Context (Strategy& strategy)
: pStrategy(&strategy)
{
}
void SetStrategy(Strategy& strategy)
{
pStrategy = &strategy;
}
int executeStrategy(int a, int b)
{
return pStrategy->execute(a,b);
}
};
int main()
{
ConcreteStrategyAdd concreteStrategyAdd;
ConcreteStrategySubstract concreteStrategySubstract;
ConcreteStrategyMultiply concreteStrategyMultiply;
Context context(concreteStrategyAdd);
int resultA = context.executeStrategy(3,4);
context.SetStrategy(concreteStrategySubstract);
int resultB = context.executeStrategy(3,4);
context.SetStrategy(concreteStrategyMultiply);
int resultC = context.executeStrategy(3,4);
std::cout << "resultA: " << resultA << "\tresultB: " << resultB << "\tresultC: " << resultC << "\n";
}
public class StrategyPatternWiki
{
public static void Main(String[] args)
{
// Prepare strategies
var normalStrategy = new NormalStrategy();
var happyHourStrategy = new HappyHourStrategy();
var firstCustomer = new CustomerBill(normalStrategy);
// Normal billing
firstCustomer.Add(1.0, 1);
// Start Happy Hour
firstCustomer.Strategy = happyHourStrategy;
firstCustomer.Add(1.0, 2);
// New Customer
var secondCustomer = new CustomerBill(happyHourStrategy);
secondCustomer.Add(0.8, 1);
// The Customer pays
firstCustomer.Print();
// End Happy Hour
secondCustomer.Strategy = normalStrategy;
secondCustomer.Add(1.3, 2);
secondCustomer.Add(2.5, 1);
secondCustomer.Print();
}
}
// CustomerBill as class name since it narrowly pertains to a customer's bill
class CustomerBill
{
private IList<double> drinks;
// Get/Set Strategy
public IBillingStrategy Strategy { get; set; }
public CustomerBill(IBillingStrategy strategy)
{
this.drinks = new List<double>();
this.Strategy = strategy;
}
public void Add(double price, int quantity)
{
this.drinks.Add(this.Strategy.GetActPrice(price) * quantity);
}
// Payment of bill
public void Print()
{
double sum = 0;
foreach (var drinkCost in this.drinks)
{
sum += drinkCost;
}
Console.WriteLine($"Total due: {sum}.");
this.drinks.Clear();
}
}
interface IBillingStrategy
{
double GetActPrice(double rawPrice);
}
// Normal billing strategy (unchanged price)
class NormalStrategy : IBillingStrategy
{
public double GetActPrice(double rawPrice) => rawPrice;
}
// Strategy for Happy hour (50% discount)
class HappyHourStrategy : IBillingStrategy
{
public double GetActPrice(double rawPrice) => rawPrice * 0.5;
}
另一个示例
在C# 中的委托遵循策略模式,其中委托定义定义了策略接口,而委托实例表示具体的策略。.NET 3.5 定义了 Func<,> 委托,可以用于快速实现策略模式,如下面的示例所示。请注意定义委托实例的 3 种不同方法。
using System;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var context = new Context<int>();
// Delegate
Func<int, int, int> concreteStrategy1 = PerformLogicalBitwiseOr;
// Anonymous Delegate
Func<int, int, int> concreteStrategy2 = delegate(int op1, int op2) { return op1 & op2; };
// Lambda Expressions
Func<int, int, int> concreteStrategy3 = (op1, op2) => op1 >> op2;
Func<int, int, int> concreteStrategy4 = (op1, op2) => op1 << op2;
context.Strategy = concreteStrategy1;
var result1 = context.Execute(8, 9);
context.Strategy = concreteStrategy2;
var result2 = context.Execute(8, 9);
context.Strategy = concreteStrategy3;
var result3 = context.Execute(8, 1);
context.Strategy = concreteStrategy4;
var result4 = context.Execute(8, 1);
}
static int PerformLogicalBitwiseOr(int op1, int op2)
{
return op1 | op2;
}
class Context<T>
{
public Func<T, T, T> Strategy { get; set; }
public T Execute(T operand1, T operand2)
{
return this.Strategy != null
? this.Strategy(operand1, operand2)
: default(T);
}
}
}
使用接口
using System;
namespace Wikipedia.Patterns.Strategy
{
// The strategy we will implement will be
// to advise on investments.
interface IHasInvestmentStrategy
{
long CalculateInvestment();
}
// Here we have one way to go about it.
class FollowTheMoon : IHasInvestmentStrategy
{
protected virtual int MoonPhase { get; set; }
protected virtual int AstrologicalSign { get; set; }
public FollowTheMoon(int moonPhase, int yourSign)
{
MoonPhase = moonPhase;
AstrologicalSign = yourSign;
}
public long CalculateInvestment()
{
if (MoonPhase == AstrologicalSign)
return 1000;
else
return 100 * (MoonPhase % DateTime.Today.Day);
}
}
// And here we have another.
// Note that each strategy may have its own dependencies.
// The EverythingYouOwn strategy needs a bank account.
class EverythingYouOwn : IHasInvestmentStrategy
{
protected virtual OtherLib.IBankAccessor Accounts { get; set; }
public EverythingYouOwn(OtherLib.IBankAccessor accounts)
{
Accounts = accounts;
}
public long CalculateInvestment()
{
return Accounts.GetAccountBalancesTotal();
}
}
// The InvestmentManager is where we want to be able to
// change strategies. This is the Context.
class InvestmentManager
{
public IHasInvestmentStrategy Strategy { get; set; }
public InvestmentManager(IHasInvestmentStrategy strategy)
{
Strategy = strategy;
}
public void Report()
{
// Our investment is determined by the current strategy.
var investment = Strategy.CalculateInvestment();
Console.WriteLine("You should invest {0} dollars!",
investment);
}
}
class Program
{
static void Main()
{
// Define some of the strategies we will use.
var strategyA = new FollowTheMoon( 8, 8 );
var strategyB = new EverythingYouOwn(
OtherLib.BankAccountManager.MyAccount);
// Our investment manager
var manager = new InvestmentManager(strategyA);
manager.Report();
// You should invest 1000 dollars!
manager.Strategy = strategyB;
manager.Report();
// You should invest 13521500000000 dollars!
}
}
}
在Common Lisp 中的示例:使用策略类
(defclass context ()
((strategy :initarg :strategy :accessor strategy)))
(defmethod execute ((c context))
(execute (slot-value c 'strategy)))
(defclass strategy-a () ())
(defmethod execute ((s strategy-a))
(print "Doing the task the normal way"))
(defclass strategy-b () ())
(defmethod execute ((s strategy-b))
(print "Doing the task alternatively"))
(execute (make-instance 'context
:strategy (make-instance 'strategy-a)))
在 Common Lisp 中使用一等函数
(defclass context ()
((strategy :initarg :strategy :accessor strategy)))
(defmethod execute ((c context))
(funcall (slot-value c 'strategy)))
(let ((a (make-instance 'context
:strategy (lambda ()
(print "Doing the task the normal way")))))
(execute a))
(let ((b (make-instance 'context
:strategy (lambda ()
(print "Doing the task alternatively")))))
(execute b))
类似于 Python 和 Scala,Falcon 支持一等函数。以下代码实现了 Python 示例中看到的基本功能。
/*#
@brief A very basic button widget
*/
class Button( label, submit_func )
label = label
on_submit = submit_func
end
// Create two instances with different strategies ...
// ... and different ways to express inline functions
button1 = Button( "Add 'em",
function(nums)
n = 0
for val in nums: n+= val
return n
end )
button2 = Button( "Join 'em", { nums => " ".merge( [].comp(nums) ) } )
// Test each button
numbers = [1: 10]
printl(button1.on_submit(numbers)) // displays "45"
printl(button2.on_submit(numbers)) // displays "1 2 3 4 5 6 7 8 9"
Fortran 2003 添加了过程指针、抽象接口以及一等函数。以下是 Python 示例的镜像。
module m_strategy_pattern
implicit none
abstract interface
!! A generic interface to a subroutine accepting array of integers
subroutine generic_function(numbers)
integer, dimension(:), intent(in) :: numbers
end subroutine
end interface
type :: Button
character(len=20) :: label
procedure(generic_function), pointer, nopass :: on_submit
contains
procedure :: init
end type Button
contains
subroutine init(self, func, label)
class(Button), intent(inout) :: self
procedure(generic_function) :: func
character(len=*) :: label
self%on_submit => func !! Procedure pointer
self%label = label
end subroutine init
subroutine summation(array)
integer, dimension(:), intent(in) :: array
integer :: total
total = sum(array)
write(*,*) total
end subroutine summation
subroutine join(array)
integer, dimension(:), intent(in) :: array
write(*,*) array !! Just write out the whole array
end subroutine join
end module m_strategy_pattern
!! The following program demonstrates the usage of the module
program test_strategy
use m_strategy_pattern
implicit none
type(Button) :: button1, button2
integer :: i
call button1%init(summation, "Add them")
call button2%init(join, "Join them")
call button1%on_submit([(i, i=1,10)]) !! Displays 55
call button2%on_submit([(i, i=1,10)]) !! Prints out the array
end program test_strategy
此 Groovy 示例是使用块的 Ruby 示例的基本端口。示例中使用 Groovy 的闭包支持来代替 Ruby 的块。
class Context {
def strategy
Context(strategy) {
this.strategy = strategy
}
def execute() {
strategy()
}
}
def a = new Context({ println 'Style A' })
a.execute() // => Style A
def b = new Context({ println 'Style B' })
b.execute() // => Style B
def c = new Context({ println 'Style C' })
c.execute() // => Style C
在Java 中的示例
/** The classes that implement a concrete strategy should implement this.
* The Context class uses this to call the concrete strategy. */
interface Strategy {
int execute(int a, int b);
}
/** Implements the algorithm using the strategy interface */
class Add implements Strategy {
public int execute(int a, int b) {
System.out.println("Called Add's execute()");
return a + b; // Do an addition with a and b
}
}
class Subtract implements Strategy {
public int execute(int a, int b) {
System.out.println("Called Subtract's execute()");
return a - b; // Do a subtraction with a and b
}
}
class Multiply implements Strategy {
public int execute(int a, int b) {
System.out.println("Called Multiply's execute()");
return a * b; // Do a multiplication with a and b
}
}
/** Configured with a ConcreteStrategy object and maintains a reference to a Strategy object */
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int a, int b) {
return this.strategy.execute(a, b);
}
}
/** Tests the pattern */
class StrategyExample {
public static void main(String[] args) {
Context context;
// Three contexts following different strategies
context = new Context(new Add());
int resultA = context.executeStrategy(3, 4);
context = new Context(new Subtract());
int resultB = context.executeStrategy(3, 4);
context = new Context(new Multiply());
int resultC = context.executeStrategy(3, 4);
System.out.println("Result A: " + resultA );
System.out.println("Result B: " + resultB );
System.out.println("Result C: " + resultC );
}
}
在Java 中的示例
/** Imports a type of lambdas taking two arguments of the same type T and returning one argument of same type T */
import java.util.function.BinaryOperator;
/** Implements and assigns to variables the lambdas to be used later in configuring Context.
* FunctionalUtils is just a convenience class, as the code of a lambda
* might be passed directly to Context constructor, as for ResultD below, in main().
*/
class FunctionalUtils {
static final BinaryOperator<Integer> add = (final Integer a, final Integer b) -> {
System.out.println("Called add's apply().");
return a + b;
};
static final BinaryOperator<Integer> subtract = (final Integer a, final Integer b) -> {
System.out.println("Called subtract's apply().");
return a - b;
};
static final BinaryOperator<Integer> multiply = (final Integer a, final Integer b) -> {
System.out.println("Called multiply's apply().");
return a * b;
};
}
/** Configured with a lambda and maintains a reference to a lambda */
class Context {
/** a variable referencing a lambda taking two Integer arguments and returning an Integer: */
private final BinaryOperator<Integer> strategy;
public Context(final BinaryOperator<Integer> lambda) {
strategy = lambda;
}
public int executeStrategy(final int a, final int b) {
return strategy.apply(a, b);
}
}
/** Tests the pattern */
public class StrategyExample {
public static void main(String[] args) {
Context context;
context = new Context(FunctionalUtils.add);
final int resultA = context.executeStrategy(3,4);
context = new Context(FunctionalUtils.subtract);
final int resultB = context.executeStrategy(3, 4);
context = new Context(FunctionalUtils.multiply);
final int resultC = context.executeStrategy(3, 4);
context = new Context((final Integer a, final Integer b) -> a * b + 1);
final int resultD = context.executeStrategy(3,4);
System.out.println("Result A: " + resultA );
System.out.println("Result B: " + resultB );
System.out.println("Result C: " + resultC );
System.out.println("Result D: " + resultD );
}
}
类似于 Python 和 Scala,JavaScript 支持一等函数。以下代码实现了 Python 示例中看到的基本功能。
var Button = function(submit_func, label) {
this.label = label;
this.on_submit = submit_func;
};
var numbers = [1,2,3,4,5,6,7,8,9];
var sum = function(n) {
var sum = 0;
for ( var a in n ) {
sum = sum + n[a];
}
return sum;
};
var a = new Button(sum, "Add numbers");
var b = new Button(function(numbers) {
return numbers.join(',');
}, "Print numbers");
a.on_submit(numbers);
b.on_submit(numbers);
Perl 具有一等函数,因此与 Python、JavaScript 和 Scala 一样,此模式可以在不定义显式子类和接口的情况下实现
sort { lc($a) cmp lc($b) } @items
策略模式可以用Moose 正式实现
package Strategy;
use Moose::Role;
requires 'execute';
package FirstStrategy;
use Moose;
with 'Strategy';
sub execute {
print "Called FirstStrategy->execute()\n";
}
package SecondStrategy;
use Moose;
with 'Strategy';
sub execute {
print "Called SecondStrategy->execute()\n";
}
package ThirdStrategy;
use Moose;
with 'Strategy';
sub execute {
print "Called ThirdStrategy->execute()\n";
}
package Context;
use Moose;
has 'strategy' => (
is => 'rw',
does => 'Strategy',
handles => [ 'execute' ], # automatic delegation
);
package StrategyExample;
use Moose;
# Moose's constructor
sub BUILD {
my $context;
$context = Context->new(strategy => 'FirstStrategy');
$context->execute;
$context = Context->new(strategy => 'SecondStrategy');
$context->execute;
$context = Context->new(strategy => 'ThirdStrategy');
$context->execute;
}
package main;
StrategyExample->new;
在PHP 中的策略模式
<?php
interface IStrategy {
public function execute();
}
class Context {
private $strategy;
public function __construct(IStrategy $strategy) {
$this->strategy = $strategy;
}
public function execute() {
$this->strategy->execute();
}
}
class ConcreteStrategyA implements IStrategy {
public function execute() {
echo "Called ConcreteStrategyA execute method\n";
}
}
class ConcreteStrategyB implements IStrategy {
public function execute() {
echo "Called ConcreteStrategyB execute method\n";
}
}
class ConcreteStrategyC implements IStrategy {
public function execute() {
echo "Called ConcreteStrategyC execute method\n";
}
}
class StrategyExample {
public function __construct() {
$context = new Context(new ConcreteStrategyA());
$context->execute();
$context = new Context(new ConcreteStrategyB());
$context->execute();
$context = new Context(new ConcreteStrategyC());
$context->execute();
}
}
new StrategyExample();
?>
PowerShell 具有称为 ScriptBlocks 的一等函数,因此可以像 Python 一样对模式进行建模,将函数直接传递给上下文,而不是定义一个包含函数的类。
Function Context ([scriptblock]$script:strategy){
New-Module -Name Context -AsCustomObject {
Function Execute { & $strategy }
}
}
$a = Context {'Style A'}
$a.Execute()
(Context {'Style B'}).Execute()
$c = Context {'Style C'}
$c.Execute()
使用 New-Module 的替代方法
Function Context ([scriptblock]$strategy){
{ & $strategy }.GetNewClosure()
}
$a = Context {'Style A'}
$a.Invoke()
& (Context {'Style B'})
$c = Context {'Style C'}
& $c
以下示例等效于上面的 C# 示例 1,但在 Python 中。
import abc
class Bill:
def __init__(self, billing_strategy: "BillingStrategy"):
self.drinks: list[float] = []
self.billing_strategy = billing_strategy
def add(self, price: float, quantity: int) -> None:
self.drinks.append(self.billing_strategy.get_act_price(price * quantity))
def __str__(self) -> str:
return f"£{sum(self.drinks)}"
class BillingStrategy(abc.ABC):
@abc.abstractmethod
def get_act_price(self, raw_price: float) -> float:
raise NotImplementedError
class NormalStrategy(BillingStrategy):
def get_act_price(self, raw_price: float) -> float:
return raw_price
class HappyHourStrategy(BillingStrategy):
def get_act_price(self, raw_price: float) -> float:
return raw_price * 0.5
def main() -> None:
normal_strategy = NormalStrategy()
happy_hour_strategy = HappyHourStrategy()
customer_1 = Bill(normal_strategy)
customer_2 = Bill(normal_strategy)
# Normal billing
customer_1.add(2.50, 3)
customer_1.add(2.50, 2)
# Start happy hour
customer_1.billing_strategy = happy_hour_strategy
customer_2.billing_strategy = happy_hour_strategy
customer_1.add(3.40, 6)
customer_2.add(3.10, 2)
# End happy hour
customer_1.billing_strategy = normal_strategy
customer_2.billing_strategy = normal_strategy
customer_1.add(3.10, 6)
customer_2.add(3.10, 2)
# Print the bills;
print(customer_1)
print(customer_2)
if __name__ == "__main__":
main()
在Python 中的第二个示例
class Strategy:
def execute(self, a, b):
pass
class Add(Strategy):
def execute(self, a, b):
return a + b
class Subtract(Strategy):
def execute(self, a, b):
return a - b
class Multiply(Strategy):
def execute(self, a, b):
return a * b
class Context:
def __init__(self, strategy):
self.strategy = strategy
def execute(self, a, b):
return self.strategy.execute(a, b)
if __name__ == "__main__":
context = None
context = Context(Add())
print "Add Strategy %d" % context.execute(10, 5)
context = Context(Subtract())
print "Subtract Strategy %d" % context.execute(10, 5)
context = Context(Multiply())
print "Multiply Strategy %d" % context.execute(10, 5)
在 Python 中的另一个示例:Python 具有一等函数,因此可以简单地将函数直接传递给上下文,而不是定义一个包含函数的方法的类,从而使用此模式。这样会丢失一些信息,因为策略的接口没有明确说明,但是通过这种方式简化了模式。
以下是在 GUI 编程中可能会遇到的示例,使用回调函数
class Button:
"""A very basic button widget."""
def __init__(self, submit_func, label):
self.on_submit = submit_func # Set the strategy function directly
self.label = label
# Create two instances with different strategies
button1 = Button(sum, "Add 'em")
button2 = Button(lambda nums: " ".join(map(str, nums)), "Join 'em")
# Test each button
numbers = range(1, 10) # A list of numbers 1 through 9
print button1.on_submit(numbers) # displays "45"
print button2.on_submit(numbers) # displays "1 2 3 4 5 6 7 8 9"
在Ruby 中的示例
class Context
def initialize(strategy)
extend(strategy)
end
end
module StrategyA
def execute
puts 'Doing the task the normal way'
end
end
module StrategyB
def execute
puts 'Doing the task alternatively'
end
end
module StrategyC
def execute
puts 'Doing the task even more alternatively'
end
end
a = Context.new(StrategyA)
a.execute #=> Doing the task the normal way
b = Context.new(StrategyB)
b.execute #=> Doing the task alternatively
a.execute #=> Doing the task the normal way
c = Context.new(StrategyC)
c.execute #=> Doing the task even more alternatively
使用块
前面的 Ruby 示例使用了典型的 OO 特性,但可以使用 Ruby 的块以更少的代码实现相同的效果。
class Context
def initialize(&strategy)
@strategy = strategy
end
def execute
@strategy.call
end
end
a = Context.new { puts 'Doing the task the normal way' }
a.execute #=> Doing the task the normal way
b = Context.new { puts 'Doing the task alternatively' }
b.execute #=> Doing the task alternatively
c = Context.new { puts 'Doing the task even more alternatively' }
c.execute #=> Doing the task even more alternatively
与 Python 一样,Scala 也支持一等函数。以下代码实现了 Python 示例中显示的基本功能。
// A very basic button widget.
class Button[T](val label: String, val onSubmit: Range => T)
val button1 = new Button("Add", _ reduceLeft (_ + _))
val button2 = new Button("Join", _ mkString " ")
// Test each button
val numbers = 1 to 9 // A list of numbers 1 through 9
println(button1 onSubmit numbers) // displays 45
println(button2 onSubmit numbers) // displays 1 2 3 4 5 6 7 8 9