Menu
Coddy logo textTech

Реализация интерфейсов

Часть раздела Объектно-ориентированное программирование путешествия по C на Coddy. Урок 41 из 61.

Теперь, когда вы понимаете, что такое interface — структура, содержащая только указатели на function, пришло время создать конкретные реализации, выполняющие этот контракт.

Вспомните наш интерфейс ILogger из предыдущего урока:

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

Чтобы реализовать этот интерфейс, мы пишем настоящие functions, соответствующие сигнатуре LogFunc, затем создаём экземпляры ILogger, назначив им эти functions:

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;
}

Каждая функция "constructor" возвращает 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 полностью отделена от реализации ведения журнала. Вы можете свободно менять logger, не изменяя никакую основную логику: в этом и заключается сила программирования на основе интерфейса.

challenge icon

Задание

Легко

Давайте создадим систему INotifier, демонстрирующую, как различные реализации могут соответствовать одному и тому же контракту interface. Вы создадите конкретные notifier — EmailNotifier и SMSNotifier, — которые соответствуют общему interface, но формируют разный output.

Вы организуете код в трёх файлах:

  • notifier.h: здесь Define interface. Create тип указателя на function с именем NotifyFunc, который принимает сообщение const char* и ничего не возвращает. Затем Define структуру INotifier, содержащую только указатель на function notify этого типа. Также Declare две функции constructor: create_email_notifier и create_sms_notifier, каждая из которых возвращает INotifier по значению.
  • notifier.c: здесь Implement конкретные функции notification и constructor. Create:
    • email_notify: выводит message в формате: [EMAIL] message
    • sms_notify: выводит message в формате: [SMS] message
    • create_email_notifier: возвращает INotifier, связанный с email_notify
    • create_sms_notifier: возвращает INotifier, связанный с sms_notify
  • main.c: здесь объедините всё вместе. Считайте тип notification и message, создайте appropriate notifier с помощью функций constructor и отправьте notification через interface.

Ваша программа получит два входных значения: тип notification (1 для email, 2 для SMS) и message для отправки.

Используйте функции constructor, чтобы создать appropriate notifier, затем вызовите notify через interface для отображения message.

Пример output, когда тип равен 1, а message — Meeting at 3pm:

[EMAIL] Meeting at 3pm

Пример output, когда тип равен 2, а message — Your code shipped:

[SMS] Your code shipped

Преимущество этого шаблона заключается в том, что main.c не нужно знать, как каждый notifier работает内部но. Он просто вызывает function interface notify, и выполняется правильная реализация. Не забудьте использовать include guards в header file.

Попробуйте сами

#include <stdio.h>
#include "notifier.h"

int main() {
    int type;
    char message[256];
    
    // Прочитать тип уведомления (1 для email, 2 для SMS)
    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: Создать соответствующий notifier на основе type
    // Использовать create_email_notifier() для type 1
    // Использовать create_sms_notifier() для type 2
    
    // TODO: Вызвать функцию notify через интерфейс
    // notifier.notify(message);
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C