策略模式
需求:银行收银系统,营业员根据客户所购买商品的单价和数量,向客户收费。其中有几种模式:原价模式、打折模式、满减模式,每一种商品在结账的时候都要选择一种模式进行对应,且模式中的值有不同。
根据需求,准备以下不同的类。
一、抽象策略接口
interface Strategy { public function getResult(); }
二、原价策略类
class Cash implements Strategy { protected $price; protected $amount; public function __construct($price,$amount) { $this->price = $price; $this->amount = $amount; } public function getResult() { return $this->price * $this->amount; } }
三、打折策略类
class Discount implements Strategy { protected $price; protected $amount; protected $rate; public function __construct($price,$amount,$rate) { $this->price = $price; $this->amount = $amount; $this->rate = $rate; } public function getResult() { return $this->price * $this->amount * $this->rate; } }
四、满减策略类
class Rebate implements Strategy { protected $price; protected $amount; protected $rule; public function __construct($price,$amount,$rule) { $this->price = $price; $this->amount = $amount; $this->rule = $rule; } public function getResult() { $money = $this->price * $this->amount; $result = self::_minusMoney($money,$this->rule); return $result; } /** * 计算满减后的金额 * @param $money * @param $rule * @return mixed */ private function _minusMoney($money,$rule) { $result = $money - floor($money/$rule[0]) * $rule[1]; return $result; } }
五、上下文类
class Context { private $strategy; public function __construct(Strategy $strategy) { $this->strategy = $strategy; } public function countResult() { return $this->strategy->getResult(); } }
六、简单工厂类
class Factory { public static function createObj($params) { $obj = null; $price = $params['price']; $amount = $params['amount']; $discount = isset($params['discount'])?$params['discount']:1; $rule = isset($params['rule'])?$params['rule']:[]; switch($params['type']) { case 1: $obj = new Cash($price,$amount); break; case 2: $obj = new Discount($price,$amount,$discount); break; case 3: $obj = new Rebate($price,$amount,$rule); break; } return $obj; } }
七、测试代码如下:
$result = 0; $params = [ 'type'=>1, 'price'=>12, 'amount'=>5 ]; $strategy_cash = new Context(Factory::createObj($params)); echo '原价商品,费用为:',$strategy_cash->countResult(); $result += $strategy_cash->countResult(); $params = [ 'type'=>2, 'price'=>12, 'amount'=>5, 'discount'=>0.8 ]; $strategy_discount = new Context(Factory::createObj($params)); echo '打折商品,费用为:',$strategy_discount->countResult(); $result += $strategy_cash->countResult(); $params = [ 'type'=>3, 'price'=>120, 'amount'=>5, 'rule'=>[300,40] ]; $strategy_rebate = new Context(Factory::createObj($params)); echo '满减商品,费用为:',$strategy_rebate->countResult(); $result += $strategy_cash->countResult(); echo '总费用为:',$result;
八、UML图如下:
总结:
1. 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
2. 当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。
3. 策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。