如何在此上下文中使用策略模式?

时间:2013-08-31 23:43:22

标签: oop strategy-pattern

首先让我说我是数学家,而不是编码员。我正在尝试编写线性求解器。我编码了10种方法。我希望用户选择她希望使用的解算器,例如options.solver_choice='CG'

现在,我在一个类中编码了所有10个方法。在这种情况下如何使用策略模式?

以前,我有10个不同的功能文件,我曾经在主程序中使用开关盒。

if options.solver_choice=='CG'
CG(A,x,b);
if options.solver_choice=='GMRES'
GMRES(A,x,b);
.
.
.

4 个答案:

答案 0 :(得分:3)

这不是最准确的答案,但你应该明白这一点。

使用策略模式,您将拥有一个实现解算器方法的求解器接口:

public interface ISolver {

    int Solve();

}

您可以根据需要实现每个求解器类:

public class Solver1 : ISolver {

    int Solve() {
        return 1;
    }

}

然后,当你需要解决时,你会传递适当的求解器类:

public int DoSolve(ISolver solver) {
    return solver.solve();
}

Foo.DoSolve(new Solver1());

答案 1 :(得分:1)

TL; DR

由于我一直都理解strategy pattern,因此基本上您可以在运行时执行类或对象的组合。实现细节因语言而异,但您应该能够通过“插入”共享接口的不同模块来交换行为。这里我在Ruby中提供一个例子。

Ruby示例

假设您要使用选择策略来确定#action方法将如何返回一组结果。您可以首先编写一些名为CG和GMRES的模块。例如:

module CG
  def action a, x, b
    { a: a, x: x, b: b }
  end
end

module GMRES
  def action a, x, b
    [a, x, b]
  end
end

然后您实例化您的对象:

class StrategyPattern
end

my_strategy = StrategyPattern.new

最后,使用所需的插件行为扩展对象。例如:

my_strategy.extend GMRES
my_strategy.action 'q', nil, 1
#=> ["q", nil, 1]

my_strategy.extend GMRES
my_strategy.action 'q', nil, 1
#=> {:a=>"q", :x=>nil, :b=>1}

有些人可能认为策略模式应该在类级别实现,而不是通过扩展类的实例来实现,但这种方式让我更容易理解并且不太可能搞砸其他需要选择其他的实例策略。

更正统的替代方法是将模块的名称传递给类构造函数。您可能希望阅读Russ Olsen的 Ruby模式,以获得更全面的处理和一些其他实现模式的方法。

答案 2 :(得分:0)

PHP示例

您定义的策略只实现名为solve()

的单一方法
class CG
{
    public function solve($a, $x, $y)
    {
       //..implementation
    }
}

class GMRES
{
    public function solve($a, $x, $y)
    {
       // implementation..
    }
}

用法:

$solver = new Solver();

$solver->setStratery(new CG());
$solver->solve(1,2,3); // the result of CG

$solver->setStrategy(new GMRES());
$solver->solve(1,2,3); // the result of GMRES

class Solver
{
     private $strategy;

     public function setStrategy($strategy)
     {
         $this->strategy = $strategy;
     }

     public function solve($a, $x, $y)
     {
         return $this->strategy->solve($a, $x, $y);
     }
}

答案 3 :(得分:0)

其他答案正确地呈现了模式,但我觉得它们不够清晰。不幸的是,我提供的链接也是如此,所以我将尝试展示战略的精神,恕我直言。

策略的主要内容是拥有一个通用程序,其中一些细节(行为)被抽象化,允许它们透明地更改。

考虑梯度下降优化算法 - 基本上,它由三个动作组成:

  • 渐变估计
  • 步骤
  • 目标函数评估

通常选择抽象和配置这些步骤中的哪一步。在这个例子中,目标函数的评估似乎不是你能以多种方式做的事情 - 你总是只是...评估函数。

因此,这引入了两个不同的策略(或策略)系列:

interface GradientStrategy
{
  double[] CalculateGradient(Function objectiveFunction, double[] point);
}
  

interface StepStrategy
{
  double[] Step(double[] gradient, double[] point);
}

当然Function类似于:

interface Function
{
  double Evaluate(double[] point);
}

interface FunctionWithDerivative : Function
{
  double[] EvaluateDerivative(double[] point);
}

然后,使用所有这些策略的求解器看起来像:

interface Solver
{
  double[] Maximize(Function objectiveFunction);
}

class GradientDescentSolver : Solver
{
  public Solver(GradientStrategy gs, StepStrategy ss)
  {
    this.gradientStrategy = gs;
    this.stepStrategy = ss;
  }

  public double[] Maximize(Function objectiveFunction)
  {
    // choosing starting point could also be abstracted into a strategy
    double[] currentPoint = ChooseStartingPoint(objectiveFunction);
    double[] bestPoint = currentPoint; 
    double bestValue = objectiveFunction.Evaluate(bestPoint);

    while (...) // termination condition could also 
                // be abstracted into a strategy
    {
      double[] gradient = this.gradientStrategy.CalculateGradient(
                            objectiveFunction,
                            currentPoint);

      currentPoint = this.stepStrategy.Step(gradient, currentPoint);

      double currentValue = objectiveFunction.Evaluate(currentPoint);

      if (currentValue > bestValue)
      {
        bestValue = currentValue;
        bestPoint = currentPoint;
      }
      else
      {
        // terminate or step back and reduce step size etc.
        // this could also be abstracted into a strategy
      }
    }

    return bestPoint;
  }

  private GradientStrategy gradientStrategy;
  private StepStrategy stepStrategy;
}

所以主要的一点是你有一些算法的大纲,并且你将这个算法的特定的一般步骤委托给策略或策略。现在你可以实现GradientStrategy只适用于FunctionWithDerivative(强制转换)并只使用函数的分析导数来获得渐变。或者你可以有另一个实现梯度估计的随机版本。注意,主求解器不需要知道如何计算梯度,它只需要渐变。同样适用于StepStrategy - 它可以是具有单步大小的典型步骤策略:

class SimpleStepStrategy : StepStrategy
{
  public SimpleStepStrategy(double stepSize)
  { 
    this.stepSize = stepSize;
  }

  double[] Step(double[] gradient, double[] point)
  {
    double[] result = new double[point.Length];

    for (int i = 0;i < result.Length;++i)
    {
      result[i] = point[i] + this.stepSize * gradient[i]; 
    }

    return result;
  }

  private double stepSize;
}

,或者是一个复杂的算法来调整步长。

还要考虑代码中注释中注明的行为:TerminationStrategyDeteriorationPolicy

名字只是例子 - 他们可能不是最好的,但我希望他们给出意图。此外,通常最好坚持使用一个版本(策略或政策)。