Концепция интерфейсов
Часть раздела Объектно-ориентированное программирование путешествия по C на Coddy. Урок 40 из 61.
До сих пор наши структуры объединяли данные и поведение: например, имя вместе с указателем на функцию. Но что, если структура содержала бы только указатели на функции? Это создаёт нечто мощное: interface.
Интерфейс определяет какие операции доступны, не уточняя, как они работают. Это контракт, в котором говорится: «любой объект, реализующий этот интерфейс, должен предоставлять эти functions».
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Структура ILogger содержит только указатель на function. У неё нет собственных данных. Она исключительно описывает поведение. Префикс «I» — это распространённое соглашение, указывающее, что это interface.
Любой код, принимающий ILogger, не заботится о том, куда попадает сообщение журнала. Оно может выводиться в консоль, записываться в файл или отправляться по сети. Вызывающий код лишь знает, что может вызвать log:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // Мы не знаем, КАК он логирует
}Это разделение — сущность полиморфизма. Функция process работает с любой реализацией logger. Можно менять поведение, не изменяя код функции. На следующем уроке ты создашь конкретные реализации, которые выполняют условия этого контракта interface.
Задание
ЛегкоДавайте создадим интерфейс ICalculator: структуру, содержащую только указатели на functions и определяющую, какие операции должен поддерживать любой калькулятор, не указывая, как именно они работают.
Вы создадите два файла, чтобы организовать свой код:
calculator.h: определите интерфейс здесь. Создайте тип указателя наfunctionс именемBinaryOp, который принимает два параметра типаintи возвращаетint. Затем определите структуруICalculator, содержащую только два указателя наfunctions:operate(типаBinaryOp) иdescribe(указатель наfunction, которая не принимает параметров и ничего не возвращает). Эта структура является исключительно интерфейсом. Она не содержит данных, только поведение.main.c: подключите заголовочный файл и реализуйте конкретныеfunctions, которые могут соответствовать интерфейсу:add: возвращает сумму двух integersmultiply: возвращает product двух integersdescribe_add: выводитAdditiondescribe_multiply: выводитMultiplication
ICalculator, настройте его на основе входных данных и используйте его для выполнения вычисления.
Ваша программа получит три входных значения: два integers и код операции (1 для сложения, 2 для умножения).
В зависимости от кода операции свяжите ICalculator с соответствующей парой functions. Затем вызовите describe через интерфейс, чтобы вывести название операции, после чего вызовите operate, чтобы вычислить и вывести result.
Пример вывода, если входные данные — 5, 3 и 1:
Addition
8Пример вывода, если входные данные — 4, 7 и 2:
Multiplication
28Главная идея заключается в том, что структура ICalculator определяет контракт: любой код, использующий её, know, что может вызвать describe и operate, не зная, какие именно functions подключены. Не забудьте использовать защиту от повторного включения в заголовочном файле.
Попробуйте сами
#include <stdio.h>
#include "calculator.h"
// TODO: Реализуйте функцию add
// Возвращает сумму двух целых чисел
// TODO: Реализуйте функцию multiply
// Возвращает произведение двух целых чисел
// TODO: Реализуйте функцию describe_add
// Выводит "Addition"
// TODO: Реализуйте функцию describe_multiply
// Prints "Multiplication"
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO: Создайте экземпляр ICalculator
// TODO: На основе op_code (1 для add, 2 для multiply),
// свяжите калькулятор с соответствующими функциями
// TODO: Вызовите describe через интерфейс
// TODO: Вызовите operate через интерфейс и выведите результат
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета8Полиморфизм
Указатели на функции в структурахСимуляция методовКонцепция интерфейсовРеализация интерфейсовПолиморфная итерацияПовторение: Greeter3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигурПотренируйтесь самостоятельно: Онлайн-компилятор C