使用策略模式,你可以把一族不同的算法(业务)封装到不同的类中,使 client 类可以在不知道具体实现的情况下选择实例化其中一个算法。策略模式有几种不同的变体,最简单的是下面这种:
第一段代码展示了一族输出算法,分别具体实现了 OutputInterface 的 load 方法,返回序列化结果,json 和数组:
<?php
interface OutputInterface
{
public function load();
}
class SerializedArrayOutput implements OutputInterface
{
public function load()
{
return serialize($arrayOfData);
}
}
class JsonStringOutput implements OutputInterface
{
public function load()
{
return json_encode($arrayOfData);
}
}
class ArrayOutput implements OutputInterface
{
public function load()
{
return $arrayOfData;
}
}
通过像上面这样把不同类型的输出算法封装起来,其他的开发者可以很容易地在不影响 client 代码的情况下添加新的输出类型。
每个具体的输出类实现了 OutputInterface —— 这有两个目的:
注意:PHP 类型约束不能用于标量类型如 int 或 string。Traits 也不允许
接下来的一小段代码展示了一个 client 类如何使用其中一个输出算法,并可以在运行时根据需要选用不同的算法。
<?php
class SomeClient
{
private $output;
public function setOutput(OutputInterface $outputType)
{
$this->output = $outputType;
}
public function loadOutput()
{
return $this->output->load();
}
}
上面的 client类有一个必须在运行时设置的私有属性,并且是“OutputInterface”类型的。 一旦这个属性被设置为具体的实例(三个输出类中之一的实例),并且 loadOutput 方法被调用,那么它的 load 方法就会被调用,返回回序列化结果或 json 或数组。
<?php $client = new SomeClient(); // 希望是个数组? $client->setOutput(new ArrayOutput()); $data = $client->loadOutput(); // 希望是个 JSON? $client->setOutput(new JsonStringOutput()); $data = $client->loadOutput();
<?php
header('Content-Type:text/html;charset=utf-8');
/**
* 策略模式演示代码
*
* 为了更好地突出“策略”,我们这里以出行为例演示,日
* 常出行大概分为以下几种工具:自驾车,公交车,地铁,火车,飞机,轮船
*
* 下面一起看代码,领会何为策略模式
*/
/**
* Interface Travel 抽象策略角色
* 约定具体方法
*/
interface Travel
{
public function go();
}
/**
* Class selfDriving 具体策略角色
* 自驾车
*/
class bySelfDriving implements Travel
{
public function go()
{
echo '我自己开着车出去玩<br>';
}
}
/**
* Class byBus具体策略角色
* 乘公交
*/
class byBus implements Travel {
public function go()
{
echo '我乘公交出去玩<br>';
}
}
/**
* Class byMetro 具体策略角色
* 乘地铁
*/
class byMetro implements Travel
{
public function go()
{
echo '我乘地铁出去玩<br>';
}
}
/**
* Class byTrain 具体策略角色
* 乘火车
*/
class byTrain implements Travel
{
public function go()
{
echo '我乘火车出去玩<br>';
}
}
/**
* Class byAirplane 具体策略角色
* 乘飞机
*/
class byAirplane implements Travel
{
public function go()
{
echo '我乘飞机出去玩<br>';
}
}
/**
* Class bySteamship 具体策略角色
* 乘轮船
*/
class bySteamship implements Travel
{
public function go()
{
echo '我乘轮船出去玩<br>';
}
}
/**
* Class Mine 环境角色
*/
class Mine{
private $_strategy;
private $_isChange = false;
/**
* 构造方法
* 此处使用到了依赖注入和类型约束的概念,详情请参考
* 1.聊一聊PHP的依赖注入(DI) 和 控制反转(IoC)
* @link https://segmentfault.com/a/1190000007209266
* 2.浅谈PHP的类型约束
* @link https://segmentfault.com/a/1190000007226476
*
* @param Travel $travel
*/
public function __construct(Travel $travel)
{
$this->_strategy = $travel;
}
/**
* 改变出行方式
*
* @param Travel $travel
*/
public function change(Travel $travel)
{
$this->_strategy = $travel;
$this->_isChange = true;
}
public function goTravel()
{
if ($this->_isChange) {
echo '现在改变主意,';
$this->_strategy->go();
} else {
$this->_strategy->go();
}
}
}
/** * 客户端使用 */ $strategy = new Mine(new byBus());// 乘公交 $strategy->goTravel(); $strategy->change(new byMetro());// 乘地铁 $strategy->goTravel(); $strategy->change(new bySelfDriving());// 自驾车 $strategy->goTravel(); // 其他根据具体应用选择实现
我乘公交出去玩 现在改变主意,我乘地铁出去玩 现在改变主意,我自己开着车出去玩
客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。