策略模式上下文中的多种策略
Multiple Strategies in Strategy Pattern Context
我对策略模式有疑问。通常策略模式是这样的:
class TaxCalculatorContext
{
private $strategy;
public function setStrategy(TaxCalculatorStrategyInterface $strategy)
{
$this->strategy = $strategy;
}
public function execute($amount)
{
return $this->strategy->calculate($amount);
}
}
class TaxCalculatorOntario implements TaxCalulatorStrategyInterface
{
public function calculate($amount)
{
...calculate ontario taxes here
}
}
class TaxCalculatorQuebec implements TaxCalculatorStrategyInterface
{
public function calculate($amount)
{
...calculate quebec taxes here
}
}
interface TaxCalculatorStrategyInterface
{
public function calculate($amount);
}
我想知道在一个上下文中使用多种策略是否是一种可接受的做法。请看下面的代码。
class TaxCalculatorContext
{
private $strategies;
public function addStrategy(TaxCalculatorStrategyInterface $strategy)
{
$this->strategies[] = $strategy;
}
public function execute($amount)
{
foreach ($this->strategies as $strategy)
{
if($strategy->canCalculate)
{
return $strategy->calculate($amount);
}
}
}
}
class TaxCalculatorOntario implements TaxCalulatorStrategyInterface
{
public function canCalculate($amount)
{
... returns true or false
}
public function calculate($amount)
{
...calculate ontario taxes here
}
}
class TaxCalculatorQuebec implements TaxCalculatorStrategyInterface
{
public function canCalculate($amount)
{
... returns true or false
}
public function calculate($amount)
{
...calculate quebec taxes here
}
}
interface TaxCalculatorStrategyInterface
{
public function canCalculate($amount);
public function calculate($amount);
}
如您所见,我没有只传递一个策略,而是在 TaxCalculatorContext 中创建了一组策略。然后,当调用 execute 方法时,策略将循环执行,并且将执行 canCalulate 方法中第一个 returns true 的策略。这是标准做法还是我应该避免?
如果它适合您的问题,那么我看不出有任何理由不适合您。如果您要将其改装到现有的策略结构中,那么您可以使用复合模式进行研究。您将使用它的方式是创建一个新的策略子类,它的构造函数或工厂接受多个 other 策略。然后,在策略本身内部,它将根据需要执行子策略。这样一来,您就不需要去更改 Strategy 接口,调用该策略的客户端代码甚至不需要知道甚至 是 多个策略。
但是,如果您不进行改造,并且拥有多种策略的想法是问题领域不可或缺的一部分,那么您可以说可以避免实施 Composite 所增加的复杂性,而只需按照您的指示进行即可。在一个事物上投入越来越多的模式并不总是必要的,或者在速度方面并不总是有效的。只要知道 Composite 是未来重构的一个选项,如果你想将多种策略的想法与客户端代码分离。
关于您给出的示例的两点评论(您可能已经知道,但以防万一):首先,正如目前编写的那样,多个策略示例将执行 each 策略,其谓词 returns 是的,不仅仅是第一个(如果你希望这种行为,你需要在计算第一个之后的 break 语句)。其次,您在应用程序周围分散的状态越多,您将拥有的潜在错误就越多。因此可能需要考虑从 Strategy::calculate() 中返回计算值,而不是像接口当前暗示的那样将其存储在策略中。这将使您的策略成为无状态仿函数,这是可取的,因为这样您就不必考虑管理它们的状态。
我对策略模式有疑问。通常策略模式是这样的:
class TaxCalculatorContext
{
private $strategy;
public function setStrategy(TaxCalculatorStrategyInterface $strategy)
{
$this->strategy = $strategy;
}
public function execute($amount)
{
return $this->strategy->calculate($amount);
}
}
class TaxCalculatorOntario implements TaxCalulatorStrategyInterface
{
public function calculate($amount)
{
...calculate ontario taxes here
}
}
class TaxCalculatorQuebec implements TaxCalculatorStrategyInterface
{
public function calculate($amount)
{
...calculate quebec taxes here
}
}
interface TaxCalculatorStrategyInterface
{
public function calculate($amount);
}
我想知道在一个上下文中使用多种策略是否是一种可接受的做法。请看下面的代码。
class TaxCalculatorContext
{
private $strategies;
public function addStrategy(TaxCalculatorStrategyInterface $strategy)
{
$this->strategies[] = $strategy;
}
public function execute($amount)
{
foreach ($this->strategies as $strategy)
{
if($strategy->canCalculate)
{
return $strategy->calculate($amount);
}
}
}
}
class TaxCalculatorOntario implements TaxCalulatorStrategyInterface
{
public function canCalculate($amount)
{
... returns true or false
}
public function calculate($amount)
{
...calculate ontario taxes here
}
}
class TaxCalculatorQuebec implements TaxCalculatorStrategyInterface
{
public function canCalculate($amount)
{
... returns true or false
}
public function calculate($amount)
{
...calculate quebec taxes here
}
}
interface TaxCalculatorStrategyInterface
{
public function canCalculate($amount);
public function calculate($amount);
}
如您所见,我没有只传递一个策略,而是在 TaxCalculatorContext 中创建了一组策略。然后,当调用 execute 方法时,策略将循环执行,并且将执行 canCalulate 方法中第一个 returns true 的策略。这是标准做法还是我应该避免?
如果它适合您的问题,那么我看不出有任何理由不适合您。如果您要将其改装到现有的策略结构中,那么您可以使用复合模式进行研究。您将使用它的方式是创建一个新的策略子类,它的构造函数或工厂接受多个 other 策略。然后,在策略本身内部,它将根据需要执行子策略。这样一来,您就不需要去更改 Strategy 接口,调用该策略的客户端代码甚至不需要知道甚至 是 多个策略。
但是,如果您不进行改造,并且拥有多种策略的想法是问题领域不可或缺的一部分,那么您可以说可以避免实施 Composite 所增加的复杂性,而只需按照您的指示进行即可。在一个事物上投入越来越多的模式并不总是必要的,或者在速度方面并不总是有效的。只要知道 Composite 是未来重构的一个选项,如果你想将多种策略的想法与客户端代码分离。
关于您给出的示例的两点评论(您可能已经知道,但以防万一):首先,正如目前编写的那样,多个策略示例将执行 each 策略,其谓词 returns 是的,不仅仅是第一个(如果你希望这种行为,你需要在计算第一个之后的 break 语句)。其次,您在应用程序周围分散的状态越多,您将拥有的潜在错误就越多。因此可能需要考虑从 Strategy::calculate() 中返回计算值,而不是像接口当前暗示的那样将其存储在策略中。这将使您的策略成为无状态仿函数,这是可取的,因为这样您就不必考虑管理它们的状态。