策略
範圍
物件
目的
行為
意圖
定義一系列演算法,封裝每個演算法,並使它們可以互換,以便客戶端和演算法能夠獨立地變化。
適用性
- 當一個物件應該使用幾種演算法之一配置時,
- 並且所有演算法都可以封裝,
- 並且一個介面涵蓋所有封裝。
結構

後果
- + 更大的靈活性,重用
- + 可以動態地改變演算法
- - 策略建立和通訊開銷
- - 策略介面不靈活
實現
- 在策略與其上下文之間交換資訊
- 透過模板進行靜態策略選擇
相關模式
- 狀態,可以啟用多個狀態,而策略只能啟用一個演算法。
- 享元,提供一個共享物件,可以同時在多個上下文中使用,而策略則專注於一個上下文。
- 裝飾器,改變物件的皮膚,而策略改變物件的內在機制。
- 組合,與策略結合使用以提高效率。
描述
假設您在一家開發策略遊戲的公司工作。讓我們假設您已經提出了以下類層次結構。所有角色都能夠行走,並且還有一種方法可以在螢幕上渲染它們。超類負責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
