策略模式初探
什么是策略模式?
定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。 《设计模式》
定义了一族算法(业务规则);
封装了每个算法;
这族的算法可互换代替(interchangeable)。 维基百科
完全无法理解有木有?
- 看一个例子:
比如说我,
我住在公司附近。
骑单车要十分钟左右。
走路需要二十分钟左右。
但是我需要根据天气来选择到底是骑单车还是走路。
下雨的话,没办法骑单车只能走路过去了。
晴天就可以睡晚一点骑单车去。
用伪代码来实现就会是这样:
class way {
// 1 晴天
// 2 下雨
public function go($weather){
if ( $weather === 1 ) {
echo '晴天可以骑单车!';
} else if ( $weather === 2 ) {
echo '下雨走路!';
}
}
}
但是,如果碰到下冰雹呢,或者台风呢?
OK,很简单,我们再去修改一下代码
class way {
// 1 晴天
// 2 下雨
// 3 冰雹
// 4 下雪
public function go($weather){
switch ( $weather ) {
case 1:
echo '骑单车';
break;
case 2:
echo '走路';
break;
case 3:
echo '打的';
break;
case 4:
echo '休假';
break;
default:
echo '不知道该怎么去';
break;
}
}
}
- 那么问题来了
以后每次增加新的方式都要去对代码进行修改吗?
假设每次都进行修改,那么长久下去,这个方法会变得臃肿不堪,并且不易维护和扩展,不符合我们软件工程的铁律--高内聚,低耦合。
那么,怎么重构它呢?
在前辈们的探索下,总结出了一个方法,这个方法就是我们现在要了解的策略模式。
interface way {
public function go();
}
//骑单车
class bike implements way {
public function go()
{
echo '骑单车';
}
}
//走路
class walk implements way {
public function go()
{
echo '走路';
}
}
// 我
class Person {
private $way;
public function __construct (way $way)
{
$this->way = $way;
}
public function change_way (way $way)
{
$this->way = $way;
}
public function go_company ()
{
$this->way->go();
}
}
$me = new Person( new bike() );
$me->go_company(); // 骑单车
$me->change_way( new walk() );
$me->go_company(); //走路
这样一来,当有新的方式出现的时候,只需要增加方法类就可以了,完全不需要改动其他的地方。
- 回顾一下
我们先设计了一个接口类way,两个去公司的方法类继承这个接口,最后由我来选择用哪个。
- 看一下定义:
定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。 《设计模式》
定义了一族算法(业务规则);
封装了每个算法;
这族的算法可互换代替(interchangeable)。 维基百科
从定义中能看出来,他们的意思其实是一样的。
首先要定义一系列或者说一族的算法进行封装。
- 什么是一系列,一族呢
拥有同一种特性的事物就是一系列或者说一族。
上面我们定义了一个接口类way, 来使继承类必须实现一个相同名字的方法go。 这些类就可以称作为一族,一系列。
这族的算法可互换代替(interchangeable)
用上面的例子来说就是:
我可以随意选择去公司的方式。
- 再来看一下定义:
定义一系列的算法,把它们一个个封装起来(继承接口way,全都实现go方法),并且使它们可互相替换(我随便用那种方式去公司)。 《设计模式》
定义了一族算法(业务规则);(定义接口类way)
封装了每个算法;(全都实现go方法)
这族的算法可互换代替(interchangeable)。(我随意用那种方法去公司) 维基百科
- 策略模式的分析
从上面的例子中发现,策略模式只是对算法进行封装(对决定怎么去公司进行封装),把算法和行为分隔开。
具体怎么去使用这些算法是由我决定的(也就是客户端),客户端必须先理解所有的算法之间的区别并且决定使用哪种算法。(也就是算法不再对天气作判断了,而是我(客户端)去做)
这在一定程度上增加了客户端的使用难度。但相应的却提高了系统的灵活性。
我们也能发现,如果多个地方都用到了某个策略,那么会在内存中产生多个相同的对象,如果数量庞大点,那对内存是一个很大的负担。我们应该用一个办法将这些重复的策略类给共享起来使用,降低内存的负担。
下篇文章将讲解如何用《享元模式》优化这一缺点。
- 何时使用策略模式
我认为当在一个需要频繁增加 else if 或者 case 的情况时,就可以考虑使用策略模式减少它们之间的耦合度,提高扩展性。
本作品采用《CC 协议》,转载必须注明作者和本文链接
那对天气的判断,在策略模式里没体现。这块逻辑怎么实现?
@gangpula 这个就是策略模式的一个缺点了,策略模式只对算法进行封装,至于什么时候用,用哪个算法需要由客户端去选择。所以说会增加客户端的使用难度。
对天气的判断也可以把
if else
去掉了,哪个天气new
哪个类就行单纯为了改善if else可能有点"过度设计" , 为了使用设计模式而使用 , 策略模式更多情况下可能是为了复用算法
@carlclone 是的,为了使用而使用是没有意义的。