实现接口
本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 41 课,共 61 课。
现在你已经了解了接口是什么,也就是只包含函数指针的结构体,接下来该创建具体实现来履行这份契约了。
回想一下我们在上一课中介绍的 ILogger 接口:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;要实现这个接口,我们需要编写符合 LogFunc 签名的实际函数,然后创建将这些函数赋给实例的 ILogger:
void console_log(const char* message) {
printf("[CONSOLE] %s\n", message);
}
void file_log(const char* message) {
printf("[FILE] %s\n", message); // 模拟文件输出
}
ILogger create_console_logger() {
ILogger logger = { console_log };
return logger;
}
ILogger create_file_logger() {
ILogger logger = { file_log };
return logger;
}每个“构造函数”函数都会返回一个接入了不同函数的 ILogger。使用这些日志记录器的代码不需要知道它收到的是哪种实现:
void do_work(ILogger* logger) {
logger->log("Starting work...");
logger->log("Work complete!");
}
int main() {
ILogger console = create_console_logger();
ILogger file = create_file_logger();
do_work(&console); // 使用 console_log
do_work(&file); // 使用 file_log
return 0;
}do_work 函数与日志实现完全解耦。你可以自由更换日志记录器,而无需更改任何核心逻辑:这就是面向接口编程的优势。
挑战
简单我们来构建一个 INotifier 系统,展示不同的实现如何满足同一接口约定。你将创建两个具体的通知器:EmailNotifier 和 SMSNotifier。它们都遵循同一个接口,但会产生不同的输出。
你将把代码分成 3 个文件:
notifier.h:在这里定义接口。创建一个名为NotifyFunc的函数指针类型,它接收一条const char*消息,不返回任何内容。然后定义一个INotifier结构体,其中只包含一个此类型的notify函数指针。另外,声明两个构造函数:create_email_notifier和create_sms_notifier,它们都按值返回一个INotifier。notifier.c:在这里实现具体的通知函数和构造函数。创建:email_notify:按以下格式输出消息:[EMAIL] messagesms_notify:按以下格式输出消息:[SMS] messagecreate_email_notifier:返回一个绑定到email_notify的INotifiercreate_sms_notifier:返回一个绑定到sms_notify的INotifier
main.c:在这里整合所有内容。读取通知类型和消息,使用构造函数创建相应的通知器,然后通过接口发送通知。
你的程序会接收两个输入:一个通知类型(1 表示电子邮件,2 表示 SMS)和一条要发送的消息。
使用构造函数创建相应的通知器,然后通过接口调用 notify 来显示消息。
当类型为 1,消息为 Meeting at 3pm 时,输出示例:
[EMAIL] Meeting at 3pm当类型为 2,消息为 Your code shipped 时,输出示例:
[SMS] Your code shipped这种模式的优势在于,main.c 不需要了解每种通知器的内部工作方式。它只需调用接口的 notify 函数,就会执行正确的实现。记得在头文件中使用 include guard。
亲手试试
#include <stdio.h>
#include "notifier.h"
int main() {
int type;
char message[256];
// 读取通知类型(1 表示邮件,2 表示短信)
scanf("%d", &type);
getchar(); // 读取并丢弃换行符
// 读取消息
fgets(message, sizeof(message), stdin);
// 如果末尾有换行符,就将其删除
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: 根据 type 创建对应的通知器
// type 为 1 时使用 create_email_notifier()
// type 为 2 时使用 create_sms_notifier()
// TODO: 通过接口调用 notify 函数
// notifier.notify(message);
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C 在线编译器