考虑这样一个场景。我们要计算两个数的值,但是算法不确定,可以是加减乘除的任意一种,甚至可以是加减乘除任意组合计算。
就实现方式来说可以有多种。比如我可以通过if else 继续算法控制
class Context {
private $_a = null;
private $_b = null;
public function __construct($a, $b) {
$this->_a = $a;
$this->_b = $b;
}
//
public function calculate($flag) {
if($flag == '1') { //加
return $this->_a + $this->_b;
}
else if($flag == '2') { //减
return $this->_a - $this->_b;
}
else if($flag == '3') { //乘
return $this->_a * $this->_b;
}
else if($flag == '4') { //除
return $this->_a / $this->_b;
}
return false;
}
}
甚至可以通过计算,分别实现加减乘除的子类
abstract class Context {
private $_a = null;
private $_b = null;
public function __construct($a, $b) {
$this->_a = $a;
$this->_b = $b;
}
//
public abstract function calculate();
}
class AddContext extends Context {
public function calculate(){
return $this->_a + $this->_b;
}
}
class SubtractContext extends Context {
public function calculate(){
return $this->_a - $this->_b;
}
}
class MultiplyContext extends Context {
public function calculate(){
return $this->_a * $this->_b;
}
}
class DivideContext extends Context {
public function calculate(){
return $this->_a / $this->_b;
}
}
在上面的方法中一个通过if else实现,虽然能达到效果,但是当算法多的时候我们需要维护大量的if else,
同时,如果要添加算法,就需要在原来的类中修改,违反了OCP原则。而继承这种方法暴露的问题则是
1.继承实现会暴露一些不必要的接口,上面只是一个calculate,如果 我如果定义方法 public Axxx function(){} 。那么这些子类是可以访问和修改的
2.父类通常至少定义了部分子类的具体表示,因为继承对子类揭示了其父类的具体实现,所以破坏了封装。子类中的实现与它的父类有如此紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类变化。
3.继承无法重复利用基本的算法,比如其他地方也要用的加减乘除的算法。 也就是说,它们也要通过继承,去实现加减乘除。这样会引起类的暴增。
因为上述原因,我们需要一个更好的方式去实现,即能减少if else的维护,又能有更好的扩展性,同时基本算法还能重复利用。
而这就需要引入今天的主角---策略模式
interface IStrategy {
public function calculate($a,$b);
}
//加
class addStrategy implements IStrategy {
public function calculate($a, $b) {
return $a + $b;
}
}
//减
class SubtractStrategy implements IStrategy {
public function calculate($a, $b) {
return $a - $b;
}
}
//乘
class MultiplyStrategy implements IStrategy {
public function calculate($a, $b) {
return $a * $b;
}
}
//除
class DivideStrategy implements IStrategy {
public function calculate($a, $b) {
return $a / $b;
}
}
class Context {
private $_a = null;
private $_b = null;
private $_strategy = null; //组合
public function __construct($a, $b) {
$this->_a = $a;
$this->_b = $b;
}
public function setStrategy(IStrategy $strategy) {//设置算法
$this->_strategy = $strategy;
}
public function calculate() {
if(is_null($this->_strategy))
return false;
return $this->_strategy->calculate($this->_a, $this->_b); // 策略
}
}
策略模式优缺点:
1.定义一个算法家族,这些算法可以相互替换,而不控制算法步骤,不依赖其他(组合,由弹性),相关算法系列 Strategy类层次为Context定义了一系列的可供重用的算法或行为
2.一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。
3.将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句
4.有一个潜在的缺点就是一个客户要选择一个合适的Strategy就必须知道这些 Strategy到底有何不同。此时可能不得不向s使用者暴露具体的实现问题。因此仅当这些不同行为变体与使用者相关的行为时 , 才需要使用Strategy模式增加了对象数目