Triển khai interface
Thuộc phần Lập trình hướng đối tượng trong hành trình C của Coddy. Bài học 41 trên 61.
Giờ bạn đã hiểu interface là gì, một struct chỉ chứa các con trỏ hàm, đã đến lúc tạo các phần triển khai cụ thể đáp ứng hợp đồng đó.
Hãy nhớ lại giao diện ILogger trong bài học trước:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Để triển khai giao diện này, chúng ta viết các hàm thực sự khớp với chữ ký LogFunc, sau đó tạo các đối tượng ILogger và gán những hàm đó cho chúng:
void console_log(const char* message) {
printf("[CONSOLE] %s\n", message);
}
void file_log(const char* message) {
printf("[FILE] %s\n", message); // Mô phỏng đầu ra của tệp
}
ILogger create_console_logger() {
ILogger logger = { console_log };
return logger;
}
ILogger create_file_logger() {
ILogger logger = { file_log };
return logger;
}Mỗi hàm khởi tạo trả về một ILogger với một hàm khác được kết nối vào. Code sử dụng các logger này không cần biết mình đã nhận được cách triển khai nào:
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); // Sử dụng console_log
do_work(&file); // Sử dụng file_log
return 0;
}Hàm do_work hoàn toàn tách biệt khỏi phần triển khai ghi nhật ký. Bạn có thể thoải mái thay đổi logger mà không cần sửa bất kỳ logic cốt lõi nào: đó là sức mạnh của việc lập trình dựa trên giao diện.
Thử thách
DễHãy xây dựng một hệ thống INotifier để minh họa cách các phần triển khai khác nhau có thể đáp ứng cùng một hợp đồng giao diện. Bạn sẽ tạo các bộ thông báo cụ thể, EmailNotifier và SMSNotifier, cả hai đều tuân theo một giao diện chung nhưng tạo ra đầu ra khác nhau.
Bạn sẽ sắp xếp code trong ba tệp:
notifier.h: Định nghĩa giao diện tại đây. Tạo một kiểu con trỏ hàm có tênNotifyFunc, nhận một thông báoconst char*và không trả về giá trị nào. Sau đó, định nghĩa một cấu trúcINotifierchỉ chứa một con trỏ hàmnotifythuộc kiểu này. Đồng thời khai báo hai hàm khởi tạo:create_email_notifiervàcreate_sms_notifier, mỗi hàm trả về mộtINotifiertheo giá trị.notifier.c: Triển khai các hàm thông báo cụ thể và hàm khởi tạo tại đây. Tạo:email_notify: in thông báo theo định dạng:[EMAIL] messagesms_notify: in thông báo theo định dạng:[SMS] messagecreate_email_notifier: trả về mộtINotifierđược gắn vớiemail_notifycreate_sms_notifier: trả về mộtINotifierđược gắn vớisms_notify
main.c: Kết hợp mọi thứ tại đây. Đọc loại thông báo và nội dung thông báo, tạo bộ thông báo phù hợp bằng các hàm khởi tạo, rồi gửi thông báo qua giao diện.
Chương trình sẽ nhận hai đầu vào: loại thông báo (1 cho email, 2 cho SMS) và một thông báo cần gửi.
Dùng các hàm khởi tạo để tạo bộ thông báo phù hợp, sau đó gọi notify qua giao diện để hiển thị thông báo.
Đầu ra mẫu khi loại là 1 và thông báo là Meeting at 3pm:
[EMAIL] Meeting at 3pmĐầu ra mẫu khi loại là 2 và thông báo là Your code shipped:
[SMS] Your code shippedƯu điểm của mẫu thiết kế này là main.c không cần biết cách mỗi bộ thông báo hoạt động bên trong. Chương trình chỉ cần gọi hàm notify của giao diện, và phần triển khai phù hợp sẽ được thực thi. Nhớ dùng include guard trong tệp tiêu đề.
Tự làm thử
#include <stdio.h>
#include "notifier.h"
int main() {
int type;
char message[256];
// Đọc loại thông báo (1 cho email, 2 cho SMS)
scanf("%d", &type);
getchar(); // loại bỏ ký tự xuống dòng
// Đọc nội dung thông báo
fgets(message, sizeof(message), stdin);
// Xóa ký tự xuống dòng ở cuối nếu có
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Tạo notifier phù hợp dựa trên type
// Dùng create_email_notifier() cho type 1
// Dùng create_sms_notifier() cho type 2
// TODO: Gọi hàm notify thông qua giao diện
// notifier.notify(message);
return 0;
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình mô-đun
Tệp tiêu đềInclude guardTệp mã nguồnHàm staticÔn tập: Máy tính mô-đun4Tính đóng gói
Khái niệm con trỏ mờĐịnh nghĩa struct mờHàm truy xuất và hàm gánKiểm tra tính hợp lệ trong hàm gánÔn tập: Hộp bí mật2Đối tượng và phương thức
Struct dưới dạng đối tượngCon trỏ 'Self'Tính đúng đắn của constCon trỏ và giá trịPhương thức hỗ trợÔn tập: trình quản lý Point5Dự án: Tài khoản ngân hàng đơn giản
Thiết lập dự ánTriển khai tài khoản8Đa hình
Con trỏ hàm trong structMô phỏng phương thứcKhái niệm interfaceTriển khai interfaceLặp đa hìnhÔn tập: Greeter3Vòng đời đối tượng
Mẫu hàm khởi tạoMẫu hàm hủyKhởi tạo trên ngăn xếpSao chép sâuÔn tập: lớp bao bọc chuỗi6Kế thừa bằng cách kết hợp
Nhúng structQuy tắc thành viên đầu tiênTruy cập thành viên lớp chaUpcastingÔn tập: hệ phân cấp hình dạngTự luyện tập: Trình biên dịch C online