Menu
CoddyTech

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.

challenge icon

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ên NotifyFunc, nhận một thông báo const char* và không trả về giá trị nào. Sau đó, định nghĩa một cấu trúc INotifier chỉ chứa một con trỏ hàm notify thuộc kiểu này. Đồng thời khai báo hai hàm khởi tạo: create_email_notifier và create_sms_notifier, mỗi hàm trả về một INotifier theo 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] message
    • sms_notify: in thông báo theo định dạng: [SMS] message
    • create_email_notifier: trả về một INotifier được gắn với email_notify
    • create_sms_notifier: trả về một INotifier được gắn với sms_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;
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch C online