大话设计模式C语言之状态模式

状态模式

状态模式 (StatePattern) 状态模式是一种行为设计模式,让你能在一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。

案例代码

上下文接口 connection.h

上下文(Context)保存了对于一个具体状态对象的引用,并会将所有与该状态相关的工作委派给它。上下文通过状态接口与状态对象交互,且会提供一个设置器用于传递新的状态对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef CONNECTION_H
#define CONNECTION_H

typedef struct Connection Connection;

/* Context API */
Connection *connection_create(void);
void connection_destroy(Connection *conn);
int connection_connect(Connection *conn);
int connection_disconnect(Connection *conn);
int connection_send(Connection *conn, const char *data);

#endif /* CONNECTION_H */


上下文实现 connection.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
58
59
#include <stdio.h>
#include <stdlib.h>
#include "connection.h"
#include "connection_state.h"

/* Context 真正结构体(对外不可见) */
struct Connection
{
const ConnectionState *state;
};

/* 状态切换(仅内部使用) */
static void connection_set_state(Connection *ctx, const ConnectionState *state)
{
ctx->state = state;
printf("[FSM] state -> %s\n", state->name);
}

/* ===== Context API ===== */
Connection *connection_create(void)
{
Connection *ctx = calloc(1, sizeof(*ctx));
if (!ctx)
{
return NULL;
}

connection_set_state(ctx, state_disconnected());
return ctx;
}

void connection_destroy(Connection *ctx)
{
if (!ctx)
return;
free(ctx);
}

int connection_connect(Connection *ctx)
{
return ctx->state->connect(ctx);
}

int connection_disconnect(Connection *ctx)
{
return ctx->state->disconnect(ctx);
}

int connection_send(Connection *ctx, const char *data)
{
return ctx->state->send(ctx, data);
}

/* ===== 提供给 State 使用的“受控接口” ===== */
void connection__set_state(Connection *ctx, const ConnectionState *state)
{
connection_set_state(ctx, state);
}

抽象工厂 gui_factory.h

抽象工厂(Abstract Factory)接口声明了一组创建各种抽象产品的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef GUI_FACTORY_H
#define GUI_FACTORY_H

#include "button.h"
#include "checkbox.h"

typedef struct GuiFactory GuiFactory;

/* 抽象工厂接口 */
Button *gui_factory_create_button(GuiFactory *self);
Checkbox *gui_factory_create_checkbox(GuiFactory *self);
void gui_factory_destroy(GuiFactory *self);

/* 工厂创建函数(静态多态) */
GuiFactory *win_factory_create(void);
GuiFactory *linux_factory_create(void);

#endif

状态抽象接口 connection_state.h

状态(State)接口会声明特定于状态的方法 这些方法应能被其他所有具体状态所理解,因为你不希望某些状态所拥有的方法永远不会被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef CONNECTION_STATE_H
#define CONNECTION_STATE_H

typedef struct Connection Connection;
typedef struct ConnectionState ConnectionState;

/* State 抽象接口 */
struct ConnectionState {
const char *name;
int (*connect)(Connection *ctx);
int (*disconnect)(Connection *ctx);
int (*send)(Connection *ctx, const char *data);
};

/* 状态实例获取函数(单例) */
const ConnectionState *state_disconnected(void);
const ConnectionState *state_connecting(void);
const ConnectionState *state_connected(void);

#endif /* CONNECTION_STATE_H */

具体状态实现

具体状态(Concrete States)会自行实现特定于状态的方法。为了避免多个状态中包含相似代码, 你可以提供一个封装有部分通用行为的中间抽象类。状态对象可存储对于上下文对象的反向引用。状态可以通过该引用从上下文处获取所需信息,并且能触发状态转移。

state_connecting.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
#include "connection_state.h"
#include <stdio.h>

void connection__set_state(Connection *, const ConnectionState *);

static int connecting_connect(Connection *ctx)
{
(void)ctx;
printf("Already connecting...\n");
return -1;
}

static int connecting_disconnect(Connection *ctx)
{
printf("Cancel connect\n");
connection__set_state(ctx, state_disconnected());
return 0;
}

static int connecting_send(Connection *ctx, const char *data)
{
(void)ctx;
(void)data;
printf("Cannot send while connecting\n");
return -1;
}

/* 模拟连接成功 */
static void connecting_success(Connection *ctx)
{
printf("Connection established\n");
connection__set_state(ctx, state_connected());
}

static const ConnectionState STATE = {
.name = "CONNECTING",
.connect = connecting_connect,
.disconnect = connecting_disconnect,
.send = connecting_send
};

const ConnectionState *state_connecting(void)
{
return &STATE;
}

/* 工业项目中通常由 IO / 事件回调触发 */
__attribute__((constructor))
static void auto_transition_example(void)
{
/* 示例中不自动调用,仅展示结构 */
}

state_connected.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 "connection_state.h"
#include <stdio.h>

void connection__set_state(Connection *, const ConnectionState *);

static int connected_connect(Connection *ctx)
{
(void)ctx;
printf("Already connected\n");
return -1;
}

static int connected_disconnect(Connection *ctx)
{
printf("Disconnecting...\n");
connection__set_state(ctx, state_disconnected());
return 0;
}

static int connected_send(Connection *ctx, const char *data)
{
printf("Send: %s\n", data);
return 0;
}

static const ConnectionState STATE = {
.name = "CONNECTED",
.connect = connected_connect,
.disconnect = connected_disconnect,
.send = connected_send
};

const ConnectionState *state_connected(void)
{
return &STATE;
}

state_disconnected.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
#include "connection_state.h"
#include <stdio.h>

/* 来自 connection.c 的内部函数 */
void connection__set_state(Connection *, const ConnectionState *);

static int disconnected_connect(Connection *ctx)
{
printf("Connecting...\n");
connection__set_state(ctx, state_connecting());
return 0;
}

static int disconnected_disconnect(Connection *ctx)
{
(void)ctx;
printf("Already disconnected\n");
return -1;
}

static int disconnected_send(Connection *ctx, const char *data)
{
(void)ctx;
(void)data;
printf("Cannot send: not connected\n");
return -1;
}

static const ConnectionState STATE = {
.name = "DISCONNECTED",
.connect = disconnected_connect,
.disconnect = disconnected_disconnect,
.send = disconnected_send
};

const ConnectionState *state_disconnected(void)
{
return &STATE;
}

测试客户端代码 main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "connection.h"

int main(void)
{
Connection *conn = connection_create();

connection_send(conn, "hello"); // 失败
connection_connect(conn); // DISCONNECTED -> CONNECTING
connection_send(conn, "hello"); // 失败
connection_disconnect(conn); // CONNECTING -> DISCONNECTED

connection_connect(conn);
/* 假设某处触发连接成功 */
/* 实际项目中通常在 IO 回调中 */

connection_destroy(conn);

while(1);
return 0;
}

总结

   如果对象需要根据自身当前状态进行不同行为,同时状态的数量非常多且与状态相关的代码会频繁或某个类需要根据成员变量的当前值改变自身行为,从而需要使用大量的条件语句时,可使用该模式。