我对策略模式有疑问。通常,策略模式如下:
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方法中返回true的第一个策略。那么这是标准做法还是我应该避免?
答案 0 :(得分:2)
如果它适合您的问题,那么我看不出您不喜欢的任何原因。如果要将其改造为现有的策略结构,则可以使用Composite模式进行调查。您使用它的方式是创建一个新的策略子类,该子类接受其构造函数或工厂的多个 other 策略。然后,在策略本身内部,它将在必要时执行子策略。这样,您无需更改策略界面,并且调用该策略的客户端代码也无需知道甚至有多个策略。
但是,如果您不进行改进,并且在问题领域中包含多种策略的想法,那么可以说可以避免实现Composite所增加的复杂性,并按照说明进行操作。在物体上抛出越来越多的图案并不总是必要的,或者就速度而言并不是有效的。只要您想将多种策略的思想与客户端代码脱钩,就可以将Composite用作将来的重构选项。
关于您给出的示例的两个说明(您可能已经知道,但以防万一):首先,如当前所写,多重策略示例将执行每个策略,其谓词返回true,而不仅仅是谓词第一个(如果希望该行为,则在计算第一个后需要一个break语句)。其次,您在应用程序中散布的状态越多,潜在的错误就越多。因此,可能需要考虑从Strategy :: calculate()返回返回的计算值,而不是像接口当前所暗示的那样将其存储在策略中。这将使您的策略成为无状态函子,这是可取的,因为这样您就不必考虑管理其状态了。