命令模式

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

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

实现:
1. 类图示例:

2. 代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//命令对象参数类
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. 更好地组织系统,易于扩展。

posted @   疯一样的狼人  阅读(176)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示