策略模式,英文全称是 Strategy Design Pattern。在 GoF 的《设计模式》一书中,它是这样定义的:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
翻译成中文就是:定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。
策略模式的结构与实现
结构
代码实现
Strategy.php
<?php
namespace DesignPatterns\Strategy\example1;
/**
* Strategy类,定义所有支持算法的公共接口 可以是抽象类或接口
* Class Strategy
* @package DesignPatterns\Strategy\example1
*/
abstract class Strategy
{
//算法方法
abstract public function algorithmInterface();
}
ConcreteStrategyA.php
<?php
namespace DesignPatterns\Strategy\example1;
/**
* ConcreteStrategy,封装了具体的算法或行为,继承于Strategy
* Class ConcreteStrategyA
* @package DesignPatterns\Strategy\example1
*/
class ConcreteStrategyA extends Strategy
{
//算法A实现方法
public function algorithmInterface()
{
echo "算法A实现";
}
}
ConcreteStrategyB.php
<?php
namespace DesignPatterns\Strategy\example1;
class ConcreteStrategyB extends Strategy
{
public function algorithmInterface()
{
echo "算法B实现";
}
}
ConcreteStrategyC.php
<?php
namespace DesignPatterns\Strategy\example1;
class ConcreteStrategyC extends Strategy
{
public function algorithmInterface()
{
echo "算法C实现";
}
}
Context.php
<?php
namespace DesignPatterns\Strategy\example1;
/**
* Context,用一个ConcreteStrategy来配置,维护一个对 Strategy 对象的引用。
* Class Context
* @package DesignPatterns\Strategy\example1
*/
class Context
{
private $strategy;
//初始化时传入具体的策略对象
public function __construct(Strategy $strategy)
{
$this->strategy = $strategy;
}
//根据具体的策略对象调用其算法的方法
public function ContextInterface()
{
$this->strategy->algorithmInterface();
}
}
运行 Client.php
<?php
namespace DesignPatterns\Strategy\example1;
require dirname(__DIR__).'/../vendor/autoload.php';
class Client
{
public function run()
{
//由于示例不用的策略最终在调用ContextInterface()所获的的结果也不尽相同
$contextA = new Context(new ConcreteStrategyA());
$contextA->ContextInterface();
echo '<br>';
$contextB = new Context(new ConcreteStrategyB());
$contextB->ContextInterface();
echo '<br>';
$contextC = new Context(new ConcreteStrategyC());
$contextC->ContextInterface();
}
}
$worker = new Client();
$worker->run();
运行结果:
策略模式的实现比较简单,我们需要定义策略抽象类或策略接口,然后让所有的具体策略实现相同的接口。然后我们在context里实例化策略类,根据需要调用不同的方法。
我们可以发现策略模式和我们之前讲过的简单工厂模式 其实十分类似。不同的是,工厂相关的模式属于创建型模式,顾名思义,这种模式是用来创建对象的,返回的是new出来的对象。要调用对象的什么方法是由客户端来决定的。而策略模式属性行为型模式,通过执行上下文,将要调用的函数方法封装了起来,客户端只需要调用执行上下文的方法就可以了。
示例
假如你需要前往机场。 你可以选择乘坐公共汽车、 预约出租车或骑自行车。 这些就是你的出行策略。 你可以根据预算或时间等因素来选择其中一种策略。
Strategy.php
<?php
namespace DesignPatterns\Strategy\example2;
/**
* 出行方式策略类
* Class Strategy
* @package DesignPatterns\Strategy\example2
*/
abstract class Strategy
{
abstract function goAirport();
}
BikeStrategy.php
<?php
namespace DesignPatterns\Strategy\example2;
/**
* 自行车出行子类
* Class BikeStrategy
* @package DesignPatterns\Strategy\example2
*/
class BikeStrategy extends Strategy
{
public function goAirport()
{
echo '骑自行车去机场,需要2个小时,花费0元';
}
}
TransitStrategy.php
<?php
namespace DesignPatterns\Strategy\example2;
/**
* 公交出行子类
* Class TransitStrategy
* @package DesignPatterns\Strategy\example2
*/
class TransitStrategy extends Strategy
{
public function goAirport()
{
echo '坐公交去机场,需要1小时,花费2元';
}
}
TaxiStrategy.php
<?php
namespace DesignPatterns\Strategy\example2;
class TaxiStrategy extends Strategy
{
public function goAirport()
{
echo '坐出租车去机场,需要0.5小时,花费30元';
}
}
Context.php
<?php
namespace DesignPatterns\Strategy\example2;
class Context
{
private $strategy;
public function __construct(Strategy $strategy)
{
$this->strategy = $strategy;
}
public function ContextInterface()
{
$this->strategy->goAirport();
}
}
运行Client.php
<?php
namespace DesignPatterns\Strategy\example2;
require dirname(__DIR__).'/../vendor/autoload.php';
class Client
{
public function run($type){
switch ($type){
case 'bike':
$TravelStrategy = new Context(new BikeStrategy());
break;
case 'transit':
$TravelStrategy = new Context(new TransitStrategy());
break;
case 'taxi':
$TravelStrategy = new Context(new TaxiStrategy());
break;
default:
throw new \Exception("不支持的方式");
break;
}
$TravelStrategy->ContextInterface();
}
}
$worker = new Client();
$worker->run('bike');
运行结果:
在本例中,我们需要在客户端根据不同的类型中 实例化 Context 以及实例化对应的具体策略。客户端与Context以及具体策略耦合在一起。如何减少客户端的耦合呢?其实我们可是策略模式与简单工厂相结合来解决这个问题
策略模式与简单工厂相结合
class ContextFactory
{
private $strategy;
//
public function __construct($type)
{
switch ($type){
case 'bike':
$this->strategy = new BikeStrategy();
break;
case 'transit':
$this->strategy = new TransitStrategy();
break;
case 'taxi':
$this->strategy = new TaxiStrategy();
break;
default:
throw new \Exception("不支持的方式");
break;
}
}
//根据具体的策略对象调用其算法的方法
public function ContextInterface()
{
$this->strategy->algorithmInterface();
}
}
更改前,客户端策略模式与简单工厂结合后,具体的出行算法与客户端分离,客户端只需要认识Context,耦合性相对更低。
总结
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中公共功能。
当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个Strategy类中,可以在使用这些行为的类中消除条件语句。
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。这本身并没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化地减轻了客户端的职责。
github示例:https://github.com/yangpanyao/design-patterns/tree/master/Strategy
评论 (0)