命令模式

问题:
如果页面必须处理很多不同的任务,就应该考虑将任务进行封装。封装之后,向系统中增加新任务就会变得简单,并且可以将系统中的各部分分离开来。这时,我们就可以使用命令模式了。

概念:
将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

实现:
1. 类图示例:

2. 代码示例:

//命令对象参数类
class CommandContext
{
	private $params = [];
	private $error = '';

	public function __construct()
	{
		$this->params = $_REQUEST;
	}

	public function addParam($key, $val)
	{
		$this->params[$key] = $val;
	}

	public function get($key)
	{
		return $this->params[$key];
	}

	public function setError($error)
	{
		$this->error = $error;
	}

	public function getError()
	{
		return $this->error;
	}
}
//命令对象基类
abstract class Command
{
	abstract public function execute(CommandContext $context);
}
//命令对象具体实现
class LoginCommand extends Command
{
	public function execute(CommandContext $context)
	{
		$manager = Registry::getAccessManager();
		$user = $context->get('username');
		$pass = $context->get('pass');
		$userObj = $manager->login($user, $pass);
		if(is_null($userObj)) {
			$context->setError($manager->getError());
			return false;
		}
		$context->addParam('user', $userObj);
		return true;
	}
}
//命令对象具体实现
class FeedbackCommand extends Command
{
	public function execute(CommandContext $context)
	{
		$msgSystem = Registry::getMessageSystem();
		$email = $context->get('email');
		$msg = $context->get('msg');
		$topic = $context->get('topic');
		$result = $msgSystem->send($email, $msg, $topic);
		if(!$result) {
			$context->setError($msgSystem->getError());
			return false;
		}
		return true;
	}
}
//命令对象异常类
class CommandNotFoundException extends Exception {}
//命令对象工厂类
class CommandFactory
{
	private static $dir = 'commands';

	public static function getCommand($action = 'default')
	{
		if(preg_match('/W/', $action)) {
			throw new Exception('illegal characters in action');
		}
		$class = ucfirst(strtolower($action)) . 'Command';
		$file = self::$dir . DIRECTORY_SEPARATOR . $class . '.php';
		if(!file_exists($file)) {
			throw new CommandNotFoundException('file not found: ' . $file);
		}
		require_once($file);
		if(!class_exists($class)) {
			throw new CommandNotFoundException('class not found: ' . $file);
		}
		$cmd = new $class();
		return $cmd;
	}
}
//命令对象调用者
class Controller
{
	private $context;

	public function __construct()
	{
		$this->context = new CommandContext();
	}

	public function getContext()
	{
		return $this->context;
	}

	public function process()
	{
		$cmd = CommandFactory::getCommand($this->context->get('action'));
		if(!$cmd->execute($this->context)) {
			//失败处理
		} else {
			//成功
		}
	}
}

//伪造用户请求测试
$controller = new Controller();
$context = $controller->getContext();
$context->addParam('action', 'login');
$context->addParam('username', 'jet');
$context->addParam('pass', '888888');
$controller->process();

注意:Command对象不应该执行太多的逻辑。它们应该负责检查输入、处理错误、缓存对象和调用其他对象来执行一些必要的操作。

效果:
1. 能比较容易地设计一个命令队列。
2. 在需要的情况下,可以较容易地将命令记入日志。
3. 允许接收请求的一方决定是否要否决请求。
4. 可以很容易地实现对请求的撤销和重做。
5. 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。
6. 促进了控制器和领域模型的分离。
7. 更好地组织系统,易于扩展。

原文地址:https://www.cnblogs.com/wujuntian/p/9658420.html