1

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

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

以前,我有 10 个不同的函数文件,我曾经使用 switch case 在主程序中使用它们。

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

4 回答 4

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());
于 2013-09-01T00:03:04.337 回答
1

TL;博士

正如我一直理解的策略模式,这个想法基本上是您在运行时执行类或对象的组合。实现细节因语言而异,但您应该能够通过“插入”共享接口的不同模块来交换行为片段。在这里,我展示了一个 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 的Design Patterns in Ruby以获得更全面的处理和一些实现该模式的其他方法。

于 2013-09-01T00:40:23.707 回答
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);
     }
}
于 2013-09-01T00:54:50.837 回答
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;
}

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

还要考虑代码中注释中提到的行为:TerminationStrategy, DeteriorationPolicy.

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

于 2013-09-01T08:03:09.927 回答