前言
软软件设计模式(Design pattern),简称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。--来自百度百科。既然设计模式有那么多好处,我们在做程序设计的时候,就应该充分考虑自己需要解决的问题是否有一个设计模式与之相似,尽量使用现有的解决方案来设计程序,避免代码重复或自己考虑不足导致设计缺陷。
这篇博文将介绍设计模式中的命令模式,它属于行为模式中的一种。在一般的程序设计当中,我们期望的达到一个目的时,会直接调用能完成这个目标任务的接口来实现我们的期望,这是一种紧耦合的依赖关系。
可是,有的时候,我们想做一件事情,但是不知道交给谁去做,也不知道这件事具体怎么做,这时候,如果还按照紧耦合的依赖关系去做,发现这完全行不通,因为我们完全不知道谁能完成这件事,以及如何完成的。举个例子,到餐厅点餐,你会按自己的喜好点一份自己喜欢的菜,可是你不知道让哪个厨师去做这道菜,也不知道这道菜是如何做成的,你需要做的,就是把你的订单交给服务员,然后等待用餐就可以了。在这个例子当中,点菜和做菜是完全隔离开的,命令模式的思想与之类似,用于把命令的发送和命令的执行隔离开来,把两者解耦合。
一、使用场景
1、认为是命令的地方,都可以使用命令模式;2、系统要求操作可以执行、撤销、重新执行等,可以考虑命令模式;
3、需要对操作进行管理,比如排队等,可以考虑使用命令模式。
二、模式结构
1.命令的抽象接口类(Command)定义命令接口,声明命令的函数接口。
2.命令的具体实现类(ConcreteCommand)
实现具体命令接口的实现类,通常包含接收者的对象,命令的接口通过调用接收者的函数接口实现相应的接口功能。
3.接收者(Receiver)
真正执行命令的对象。
4.调用者(Invoker)
使用命令对象的入口,要求命令对象执行请求。简单的调用者包含一个命令对象,执行调用动作时调用命令对象的执行函数;复杂一点的调用者可以管理多个命令对象,它提供添加命令对象接口和调用接口,其内部可以把命令对象排队,执行时依序执行命令对象。
5.客户对象(Client)
创建命令对象实例,把接收者对象设置到命令对象中。
三、C语言实现命令模式
命令模式的结构是基于面向对象思想设计的模式结构,抽象基类定义统一的函数接口,具体子类实现基类定义的接口;命令对象通过构造函数把接收者设置到命令对象当中;调用者对象通过构造函数或者设置接口把命令对象设置到调用者对象当中。由于面向对象方式实现命令模式的例子比较多,这里就不使用面向对象语言实现一份命令模式了,改为使用C语言来实现命令模式。C语言是面向过程语言,通过数据结构和算法编程,抽象接口通过函数指针实现,对象的包含关系通过结构体的成员来实现。对象通过结构体和一组相关函数来实现类对象的功能。
这里以餐厅点餐作为一个例子来说明命令模式的应用。
1、接收者–厨师
厨师是命令的执行者,负责命令的执行,即炒菜。typedef void (*_cook_t)();
typedef struct _cooker_t {
_cook_t cook;
} cooker_t;
void cook_noodles();
void cook_vermicelli();
void cook_noodles() {
printf("cook noodles.\n");
}
void cook_vermicelli() {
printf("cook vermicelli.\n");
}
2、命令–订单
订单即命令模式中的命令。typedef void (*execute_t)(void* ctx);
typedef struct _order_t {
char info[100];
cooker_t cooker;
execute_t execute;
}order_t;
void execute_order(void* ctx);
void execute_order(void* ctx) {
cooker_t* cooker = (cooker_t*)ctx;
if (cooker != NULL) {
cooker->cook();
}
}
3、调用者–服务员
服务员的角色即是调用者角色。负责收集订单和处理订单。typedef struct _order_list_t {
order_t* order;
struct _order_list_t* next_order;
} order_list_t;
typedef struct _waiter_t{
order_list_t* order_list;
} waiter_t;
void init_waiter(waiter_t* waiter);
void add_order(waiter_t* waiter, order_list_t* order_node);
void execute_orders(waiter_t* waiter);
void init_waiter(waiter_t* waiter) {
waiter->order_list = NULL;
}
void add_order(waiter_t* waiter, order_list_t* order_node) {
order_list_t* next_order = waiter->order_list;
if (next_order == NULL) {
waiter->order_list = order_node;
} else {
while (next_order->next_order != NULL) {
next_order = next_order->next_order;
}
next_order->next_order = order_node;
}
}
void execute_orders(waiter_t* waiter) {
order_list_t* next_order = waiter->order_list;
while (next_order != NULL) {
next_order->order->execute(&next_order->order->cooker);
next_order = next_order->next_order;
}
waiter->order_list = NULL;
}
4、Client–餐厅
餐厅作为客户端的载体,提供厨师、服务员及菜单。这里就直接写在main函数中了。 cooker_t noodles_cooker;
cooker_t vermicelli_cooker;
order_t order_noodles;
order_t order_vermicelli;
waiter_t waiter;
order_list_t noodles_order_node;
order_list_t vermicelli_order_node;
noodles_cooker.cook = cook_noodles;
vermicelli_cooker.cook = cook_vermicelli;
order_noodles.cooker = noodles_cooker;
order_noodles.execute = execute_order;
order_vermicelli.cooker = vermicelli_cooker;
order_vermicelli.execute = execute_order;
init_waiter(&waiter);
noodles_order_node.order = &order_noodles;
noodles_order_node.next_order = NULL;
vermicelli_order_node.order = &order_vermicelli;
vermicelli_order_node.next_order = NULL;
/*点餐*/
printf("select noodles.\n");
add_order(&waiter, &noodles_order_node);
printf("select vermicelli.\n");
add_order(&waiter, &vermicelli_order_node);
execute_orders(&waiter);
5、执行结果
客户点餐,厨师根据点餐订单加工食物。