大话设计模式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 ;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.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" 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); } 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); } 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 ;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
具体状态实现
具体状态(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; } __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> 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); connection_send(conn, "hello" ); connection_disconnect(conn); connection_connect(conn); connection_destroy(conn); while (1 ); return 0 ; }
总结 如果对象需要根据自身当前状态进行不同行为,同时状态的数量非常多且与状态相关的代码会频繁或某个类需要根据成员变量的当前值改变自身行为,从而需要使用大量的条件语句时,可使用该模式。