策略(Strategy)模式
策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。此模式和工厂方法模式有相似之处,工厂方法是根据环境的不同return不同的对象.而策略模式不return对象,是进一步计算返回结果.
策略模式的结构
环境(Context)角色:持有一个Strategy类的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
应用案例
场景 : 计算器 使用策略模式之前你可能用if else 或者switch 来做. if(加法){ echo $a + $b ; }else if(减法){ echo $a - $b; }else if(乘法){ echo $a * $b; }else{ echo $a / $b; } 问题在哪? 1 . 如果要添加一种算法,如 "取模" 那么对if else 就要进行修改.违反了面向对象的开闭原则(代码新增可以,修改不行). 2 . 逻辑判断和算法耦合太高.违反了面向对象的低耦合原则.
解决方法 - 策略模式
每一种算法都封装成一个算法类,最终结果又算法类计算后给出.
案例实现
<?php //算法接口 interface algorm{ public function calc($a,$b); } //加法 class addAlgorm implements algorm{ public function calc($a,$b){ return $a+$b; } } //减法 class subAlgorm implements algorm{ public function calc($a,$b){ return $a-$b; } } //乘法 class multAlgorm implements algorm{ public function calc($a,$b){ return $a*$b; } } //除法 class diviAlgorm implements algorm{ public function calc($a,$b){ return $a/$b; } } // 获取计算结果 class math{ public static function getNum($algorm,$a,$b){ $class = $algorm.'Algorm'; $ob = new $class; return $ob->calc($a,$b); } } //只要传入对应的算法名.就可以或者计算后的值 echo math::getNum('add',10,20); ?>
在什么情况下应当使用策略模式
在下面的情况下应当考虑使用策略模式:
1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2. 一 个系统需要动态地在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算 法类均有统一的接口,由于多态性原则,客户端可以选择使用任何一个具体算法类,并只持有一个数据类型是抽象算法类的对象。
3. 一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的复杂的和只与算法有关的数据。
4. 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象设计的概念。
假设现在 要设计一个贩卖各类书籍的电子商务网站的购物车(Shopping Cat)系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的教材类图书实行每本一元的折扣;对 连环画类图书提供每本7%的促销折扣,而对非教材类的计算机图书有3%的折扣;对其余的图书没有折扣。由于有这样复杂的折扣算法,使得价格计算问题需要系 统地解决。
使 用策略模式可以把行为和环境分割开来。环境类负责维持和查询行为类,各种算法则在具体策略类 (ConcreteStrategy)中提供。由于算法和环境独立开来,算法的增减、修改都不会影响环境和客户端。当出现新的促销折扣或现有的折扣政策出 现变化时,只需要实现新的策略类,并在客户端登记即可。策略模式相当于"可插入式(Pluggable)的算法"。