大话设计模式C语言之命令模式

命令模式

命令模式(Command Pattern)是一种行为设计模式它可将请求转换为一个包含与请求相关的所有信息的独立对象。该转换让你能根据不同的请求将方法参数化、延迟请求执行或将其放入队列中,且能实现可撤销操作。

案例代码

发送者/触发者

发送者(Sender)或称 “触发者(Invoker)” 类负责对请求进行初始化,其中必须包含一个成员变量来存储对于命令对象的引用。发送者触发命令,而不向接收者直接发送请求。注意,发送者并不负责创建命令对象:它通常会通过构造函数从客户端处获得预先生成的命令,这里使用命令队列实现。

命令队列接口 command_queue.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef COMMAND_QUEUE_H
#define COMMAND_QUEUE_H

#include "command.h"

typedef struct CommandQueue CommandQueue;

CommandQueue *command_queue_create(int capacity);
int command_queue_push(CommandQueue *queue, Command *cmd);
void command_queue_execute_all(CommandQueue *queue);
void command_queue_undo_all(CommandQueue *queue);
void command_queue_destroy(CommandQueue *queue);

#endif

命令队列实现 command_queue.c
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
#include <stdlib.h>
#include "command_queue.h"

struct CommandQueue {
Command **buffer;
int capacity;
int size;
};

CommandQueue *command_queue_create(int capacity)
{
CommandQueue *q = malloc(sizeof(CommandQueue));
if (!q) return NULL;

q->buffer = malloc(sizeof(Command *) * capacity);
if (!q->buffer) {
free(q);
return NULL;
}

q->capacity = capacity;
q->size = 0;
return q;
}

int command_queue_push(CommandQueue *queue, Command *cmd)
{
if (!queue || !cmd) return -1;
if (queue->size >= queue->capacity) return -1;

queue->buffer[queue->size++] = cmd;
return 0;
}

void command_queue_execute_all(CommandQueue *queue)
{
for (int i = 0; i < queue->size; ++i)
command_execute(queue->buffer[i]);
}

void command_queue_undo_all(CommandQueue *queue)
{
for (int i = queue->size - 1; i >= 0; --i)
command_undo(queue->buffer[i]);
}

void command_queue_destroy(CommandQueue *queue)
{
if (!queue) return;

for (int i = 0; i < queue->size; ++i)
command_destroy(queue->buffer[i]);

free(queue->buffer);
free(queue);
}

命令

抽象命令接口 command.h
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef COMMAND_H
#define COMMAND_H

typedef struct Command Command;

/* 对外接口 */
void command_execute(Command *cmd);
void command_undo(Command *cmd);
void command_destroy(Command *cmd);

#endif

命令内部结构 command_internal.h
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef COMMAND_INTERNAL_H
#define COMMAND_INTERNAL_H

#include "command.h"

struct Command {
void (*execute)(Command *self);
void (*undo)(Command *self);
void (*destroy)(Command *self);
};

#endif

抽象命令实现 command.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "command_internal.h"

void command_execute(Command *cmd)
{
if (cmd && cmd->execute)
cmd->execute(cmd);
}

void command_undo(Command *cmd)
{
if (cmd && cmd->undo)
cmd->undo(cmd);
}

void command_destroy(Command *cmd)
{
if (cmd && cmd->destroy)
cmd->destroy(cmd);
}

具体命令

具体命令(Concrete Commands)会实现各种类型的请求。具体命令自身并不完成工作,而是会将调用委派给一个业务逻辑对象。接收对象执行方法所需的参数可以声明为具体命令的成员变量。可以将命令对象设为不可变,仅允许通过构造函数对这些成员变量进行初始化。

具体命令 LED_ON
led_on_command.h
1
2
3
4
5
6
7
8
9
10
#ifndef LED_ON_COMMAND_H
#define LED_ON_COMMAND_H

#include "command.h"
#include "led.h"

Command *led_on_command_create(Led *led);

#endif

led_on_command.c
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
#include <stdlib.h>
#include "command_internal.h"
#include "led_on_command.h"

typedef struct {
Command base;
Led *led;
} LedOnCommand;

static void execute(Command *cmd)
{
LedOnCommand *self = (LedOnCommand *)cmd;
led_on(self->led);
}

static void undo(Command *cmd)
{
LedOnCommand *self = (LedOnCommand *)cmd;
led_off(self->led);
}

static void destroy(Command *cmd)
{
free(cmd);
}

Command *led_on_command_create(Led *led)
{
if (!led) return NULL;

LedOnCommand *cmd = malloc(sizeof(LedOnCommand));
if (!cmd) return NULL;

cmd->base.execute = execute;
cmd->base.undo = undo;
cmd->base.destroy = destroy;
cmd->led = led;

return (Command *)cmd;
}

具体命令 LED_OFF
led_off_command.h
1
2
3
4
5
6
7
8
9
10
#ifndef LED_OFF_COMMAND_H
#define LED_OFF_COMMAND_H

#include "command.h"
#include "led.h"

Command *led_off_command_create(Led *led);

#endif

led_off_command.c
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
#include <stdlib.h>
#include "command_internal.h"
#include "led_off_command.h"

typedef struct {
Command base;
Led *led;
} LedOffCommand;

static void execute(Command *cmd)
{
LedOffCommand *self = (LedOffCommand *)cmd;
led_off(self->led);
}

static void undo(Command *cmd)
{
LedOffCommand *self = (LedOffCommand *)cmd;
led_on(self->led);
}

static void destroy(Command *cmd)
{
free(cmd);
}

Command *led_off_command_create(Led *led)
{
if (!led) return NULL;

LedOffCommand *cmd = malloc(sizeof(LedOffCommand));
if (!cmd) return NULL;

cmd->base.execute = execute;
cmd->base.undo = undo;
cmd->base.destroy = destroy;
cmd->led = led;

return (Command *)cmd;
}

接收者

接收者(Receiver)类包含部分业务逻辑。几乎任何对象都可以作为接收者。绝大部分命令只处理如何将请求传递到接收者的细节,接收者自己会完成实际的工作。

LED设备接口 led.h
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef LED_H
#define LED_H

typedef struct Led Led;

Led *led_create(int id);
void led_on(Led *led);
void led_off(Led *led);
void led_destroy(Led *led);

#endif

LED设备实现 led.c
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
#include <stdio.h>
#include <stdlib.h>
#include "led.h"

struct Led {
int id;
int state;
};

Led *led_create(int id)
{
Led *led = malloc(sizeof(Led));
if (!led) return NULL;
led->id = id;
led->state = 0;
return led;
}

void led_on(Led *led)
{
if (!led) return;
led->state = 1;
printf("LED %d ON\n", led->id);
}

void led_off(Led *led)
{
if (!led) return;
led->state = 0;
printf("LED %d OFF\n", led->id);
}

void led_destroy(Led *led)
{
free(led);
}

测试代码 main.c

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
#include "led.h"
#include "led_on_command.h"
#include "led_off_command.h"
#include "command_queue.h"

int main(void)
{
Led *led1 = led_create(1);

Command *on_cmd = led_on_command_create(led1);
Command *off_cmd = led_off_command_create(led1);

CommandQueue *queue = command_queue_create(10);

command_queue_push(queue, on_cmd);
command_queue_push(queue, off_cmd);

command_queue_execute_all(queue);
command_queue_undo_all(queue);

command_queue_destroy(queue);
led_destroy(led1);

while (1);
return 0;
}

总结

   命令模式体现了单一职责原则——你可以解耦触发和执行操作的类。以及开闭原则——你可以在不修改已有客户端代码的情况下在程序中创建新的命令。命令模式还可以实现撤销和恢复功能,实现操作的延迟执行或是可以将一组简单命令组合成一个复杂命令。