策略

设计模式 / 行为型模式
image

type
  //the algorithm is swappable
  IStrategy = interface
    function DoOperation(
      A, B: Integer): Integer;
  end;

  TAddStrategy = class(TInterfacedObject,
    IStrategy)
    function DoOperation(
      A, B: Integer): Integer;
  end;

  TSubtractStrategy = class(
    TInterfacedObject, IStrategy)
    function DoOperation(
      A, B: Integer): Integer;
  end;

  //context holds a strategy and
  //only calls it
  TCalc = class
  private
    FStrategy: IStrategy;
  public
    function Execute(
      A, B: Integer): Integer;
    procedure SetStrategy(
      const Strategy: IStrategy);
  end;

function TAddStrategy.DoOperation(
  A, B: Integer): Integer;
begin
  Result := A + B;
end;

function TSubtractStrategy.DoOperation(
  A, B: Integer): Integer;
begin
  Result := A - B;
end;

function TCalc.Execute(
  A, B: Integer): Integer;
begin
  if FStrategy = nil then
    Result := 0
  else
    Result := FStrategy.DoOperation(A, B);
end;

procedure TCalc.SetStrategy(
  const Strategy: IStrategy);
begin
  FStrategy := Strategy;
end;

var
  Calc: TCalc;
begin
  Calc := TCalc.Create;
  WriteLn('result1 is ', Calc.Execute(5, 3));
  //result1 is 0

  Calc.SetStrategy(TAddStrategy.Create);
  WriteLn('result2 is ', Calc.Execute(5, 3));
  //result2 is 8

  Calc.SetStrategy(TSubtractStrategy.Create);
  WriteLn('result3 is ', Calc.Execute(5, 3));
  //result3 is 2
end.


Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Read more

问题。商店计算运费:按重量、按距离或按固定价格。如果一个方法用一连串条件包含所有方式,每增加一种方式它就膨胀一次。修改一种方式可能破坏其他方式,而且任何一种方式都无法单独测试。

思路。完成工作的每种方式都变成一个单独的对象,称为策略。所有策略的形式相同:输入相同,结果的种类也相同。需要完成这项工作的对象称为上下文,它持有一个策略并调用它,却不知道自己持有的是哪一个。客户端代码选择策略,并可以随时替换它。

如何阅读示例。每个具体策略都是一种算法,它们都接收相同的输入。客户端代码选择一个策略。使用策略的代码只是调用它,并不知道自己收到的是哪一个。在大多数示例中,每换一次策略,同样的两个数就得出不同的结果。其中大多数示例还会针对没有策略的情况给出答案。在某些语言中,策略只是一个函数。

何时使用。当完成一项工作有多种方式、而选择取决于用户、数据或设置时,使用策略模式。只有一种不会变化的算法时不要使用它。策略模式与状态模式看起来相似,区别在于由谁做出选择。这里由客户端代码从外部选择算法,各个策略彼此一无所知。在状态模式中,对象自己从一个状态转到另一个状态。

常见错误。让上下文检查自己持有的是哪个策略。这样条件判断又回来了,模式也就失去了意义。上下文只应调用策略。