大话设计模式C语言之外观模式

外观模式

外观 (Facade) 提供了一种访问特定子系统功能的便捷方式,其了解如何重定向客户端请求,知晓如何操作一切活动部件。

案例代码

外观接口 facade.h

抽象类(Abstract­Class)会声明作为算法步骤的方法,以及依次调用它们的实际模板方法。算法步骤可以被声明为抽象类型,也可以提供一些默认实现。

  外观模式提供一个统一的高层接口使子系统更容易使用降低依赖复杂性。通过initialize_system、shutdown_system 和 read_data 这几个函数,客户端可以控制系统的行为,而不需要关心具体的子系统实现。每个函数的实现实际上都在内部调用了多个低级别的操作(例如硬件初始化、传感器校准等)。

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

typedef struct Facade Facade;

// 公共接口
Facade *create_facade();
void destroy_facade(Facade *facade);

void initialize_system(Facade *facade);
void shutdown_system(Facade *facade);
void read_data(Facade *facade);

#endif // FACADE_H

外观接口实现 facade.c

  在这里定义了一个struct Facade,但是没有暴露它的具体内容。外部代码(例如 main.c)只能通过指针类型 Facade * 来引用该结构体,而无法访问其内部字段。Facade 的创建和销毁通过 create_facade 和 destroy_facade 函数来进行。

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
#include <stdio.h>
#include <stdlib.h>
#include "facade.h"
#include "hardware_control.c"
#include "sensor_manager.c"
#include "logger.c"

// 定义一个结构体来封装内部实现
struct Facade {
// 可以在这里添加需要的内部状态或配置
};

// 外部提供的接口
Facade *create_facade() {
// 创建外观对象
Facade *facade = (Facade *)malloc(sizeof(Facade));
return facade;
}

void destroy_facade(Facade *facade) {
free(facade);
}

void initialize_system(Facade *facade) {
(void)facade; // 防止编译警告
initialize_hardware();
calibrate_sensors();
log_event("System initialized");
}

void shutdown_system(Facade *facade) {
(void)facade; // 防止编译警告
shutdown_hardware();
log_event("System shut down");
}

void read_data(Facade *facade) {
(void)facade; // 防止编译警告
read_sensor_data();
log_event("Sensor data read");
}

复杂子系统

复杂子系统 (Complex Subsystem) 由不同对象构成。如果要用这些对象完成有意义的工作,你必须深入了解子系统的实现细节,比如按照正确顺序初始化对象和为其提供正确格式的数据。子系统类不会意识到外观的存在,它们在系统内运作并且相互之间可直接进行交互。

  通过将 hardware_control.c、sensor_manager.c 和 logger.c 中的函数声明为 static,使这些函数只能在对应的 .c 文件内部使用,外部无法直接调用,从而隐藏了底层实现的细节。

hardware_control.c
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

static void initialize_hardware() {
printf("Hardware initialized.\n");
}

static void shutdown_hardware() {
printf("Hardware shut down.\n");
}

sensor_manager.c
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

static void read_sensor_data() {
printf("Reading sensor data...\n");
}

static void calibrate_sensors() {
printf("Calibrating sensors...\n");
}

logger.c
1
2
3
4
5
6
#include <stdio.h>

static void log_event(const char *event) {
printf("Log: %s\n", event);
}

测试代码 main.c

  由于客户端无法访问 Facade 结构体的内部实现(只能够通过指针与之交互),这个方式有效实现了外观模式中的封装和抽象。用户只需知道如何通过外观接口与系统交互,而无需关心系统的具体实现。

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

int main() {
// 创建外观对象
Facade *facade = create_facade();

// 使用外观类的接口
initialize_system(facade);
read_data(facade);
shutdown_system(facade);

// 销毁外观对象
destroy_facade(facade);

while(1);
return 0;
}

总结

  外观模式虽然可以让自己的代码独立于复杂子系统但是外观类对象本身可能成为与程序中所有类都耦合的上帝对象。而且不符合开闭原则,如果需要扩展子系统必须修改外观类。