策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化,即封装变化的算法。
策略模式的本质是将算法封装起来。下面看一个常见的代码
<?php
/**
* @param string $driver
* @param string $msg
* @datetime 2020/7/17 5:42 PM
* @author roach
* @email jhq0113@163.com
*/
function log($driver, $msg) {
switch ($driver) {
case 'db':
/**
* @var \PDO $db
*/
$stmt = $db->prepare('INSERT INTO `roach`(`msg`)VALUES(?)');
$stmt->execute([ $msg ]);
case 'redis':
/**
* @var \Redis $redis
*/
$redis->lPush('roach:log', $msg);
default:
file_put_contents('/tmp/roach.log', $msg, FILE_APPEND| LOCK_EX);
}
}
以上我们实现了记录日志的简单基本功能,调用端提供
driver
和msg
参数即可,调用api
很简单,log
方法里的实现也就用了一个switch
,这里有什么问题呢?
以上代码看着很简单,没什么大问题,但是我们考虑一下,是否对修改关闭了,当我们需要修改某一个
driver
记录日志的细节时,都需要修改log
方法,可见没有对修改关闭;再看一下,如果我们需要增加kafka
驱动,还是需要修改log
方法,可见也不是对扩展开放,那么怎么修改呢?
这里就用到了策略模式,下面是应用策略模式实现一个打日志功能
<?php
/**
* Class LogContext
* @datetime 2020/7/17 5:51 PM
* @author roach
* @email jhq0113@163.com
*/
class LogContext
{
/**
* @var ILog
* @datetime 2020/7/17 5:51 PM
* @author roach
* @email jhq0113@163.com
*/
private $_driver;
/**
* @param ILog $driver
* @datetime 2020/7/17 5:51 PM
* @author roach
* @email jhq0113@163.com
*/
public function setDriver(ILog $driver)
{
$this->_driver = $driver;
}
/**
* @param $msg
* @datetime 2020/7/17 5:52 PM
* @author roach
* @email jhq0113@163.com
*/
public function log($msg)
{
$this->_driver->log($msg);
}
}
/**
* Interface ILog
* @datetime 2020/7/17 5:50 PM
* @author roach
* @email jhq0113@163.com
*/
interface ILog
{
/**
* @param string $msg
* @return mixed
* @datetime 2020/7/17 5:50 PM
* @author roach
* @email jhq0113@163.com
*/
public function log($msg);
}
/**
* Class Redis
* @datetime 2020/7/17 5:52 PM
* @author roach
* @email jhq0113@163.com
*/
class Redis implements ILog
{
/**
* @param string $msg
* @return mixed|void
* @datetime 2020/7/17 5:52 PM
* @author roach
* @email jhq0113@163.com
*/
public function log($msg)
{
/**
* @var \Redis $redis
*/
$redis->lPush('roach:log', $msg);
}
}
/**
* Class File
* @datetime 2020/7/17 5:53 PM
* @author roach
* @email jhq0113@163.com
*/
class File implements ILog
{
/**
* @param string $msg
* @return mixed|void
* @datetime 2020/7/17 5:53 PM
* @author roach
* @email jhq0113@163.com
*/
public function log($msg)
{
file_put_contents('/tmp/roach.log', $msg, FILE_APPEND| LOCK_EX);
}
}
/**
* Class Db
* @datetime 2020/7/17 5:54 PM
* @author roach
* @email jhq0113@163.com
*/
class Db implements ILog
{
/**
* @param string $msg
* @return mixed|void
* @datetime 2020/7/17 5:54 PM
* @author roach
* @email jhq0113@163.com
*/
public function log($msg)
{
/**
* @var \PDO $db
*/
$stmt = $db->prepare('INSERT INTO `roach`(`msg`)VALUES(?)');
$stmt->execute([ $msg ]);
}
}
以上代码是一个策略模式的实现例程,调用端如果想记录日志,通过调用
LogContext
的log
方法即可,在调用log
方法前需要调用setDriver
初始化_driver
实例。现在我们再来看是否对修改关闭了,当我们想修改文件日志行为,直接修改类File
即可,其他的driver
不受影响;再看是否对扩展开放,当我们想把日志记录到Kafka
里,我们增加Kafka
类即可,其他driver
不受影响。
关于策略模式,你学会了吗?