Реализация интерфейсов
Часть раздела Объектно-ориентированное программирование путешествия по 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, не изменяя никакую основную логику: в этом и заключается сила программирования на основе интерфейса.
Задание
ЛегкоДавайте создадим систему INotifier, демонстрирующую, как различные реализации могут соответствовать одному и тому же контракту interface. Вы создадите конкретные notifier — EmailNotifier и SMSNotifier, — которые соответствуют общему interface, но формируют разный output.
Вы организуете код в трёх файлах:
notifier.h: здесь Define interface. Create тип указателя на function с именемNotifyFunc, который принимает сообщениеconst char*и ничего не возвращает. Затем Define структуруINotifier, содержащую только указатель на functionnotifyэтого типа. Также Declare две функции constructor:create_email_notifierиcreate_sms_notifier, каждая из которых возвращаетINotifierпо значению.notifier.c: здесь Implement конкретные функции notification и constructor. Create:email_notify: выводит message в формате:[EMAIL] messagesms_notify: выводит message в формате:[SMS] messagecreate_email_notifier: возвращаетINotifier, связанный сemail_notifycreate_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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета8Полиморфизм
Указатели на функции в структурахСимуляция методовКонцепция интерфейсовРеализация интерфейсовПолиморфная итерацияПовторение: Greeter3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигурПотренируйтесь самостоятельно: Онлайн-компилятор C