Menu
Coddy logo textTech

Концепция интерфейсов

Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 40 из 61.

До сих пор в наших структурах данные и поведение были смешаны — например, имя вместе с указателем на функцию. Но что, если бы структура содержала только указатели на функции? Это создает нечто мощное: интерфейс.

Интерфейс определяет, какие операции доступны, без указания того, как они работают. Это контракт, который гласит: «любой объект, реализующий этот интерфейс, должен предоставлять эти функции».

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

typedef struct {
    LogFunc log;
} ILogger;

Структура ILogger не содержит ничего, кроме указателя на функцию. У неё нет собственных данных — она чисто описывает поведение. Префикс «I» — это общепринятое соглашение, указывающее на то, что это интерфейс.

Любой код, который принимает ILogger, не заботится о том, куда направляется лог-сообщение. Оно может выводиться в консоль, записываться в файл или отправляться по сети. Вызывающая сторона просто знает, что она может вызвать log:

void process(ILogger* logger, const char* msg) {
    logger->log(msg);  // Мы не знаем, КАК оно логирует
}

Это разделение является сутью полиморфизма. Функция process работает с любой реализацией логгера — вы можете менять поведение, не изменяя код функции. В следующем уроке вы создадите конкретные реализации, которые соответствуют этому контракту интерфейса.

challenge icon

Задание

Легко

Давайте создадим интерфейс ICalculator — структуру, которая содержит только указатели на функции, определяя, какие операции должен поддерживать любой калькулятор, не уточняя, как именно они работают.

Для организации кода вы создадите два файла:

  • calculator.h: Определите здесь свой интерфейс. Создайте тип указателя на функцию с именем BinaryOp, который принимает два параметра типа int и возвращает int. Затем определите структуру ICalculator, которая содержит только два указателя на функции: operate (типа BinaryOp) и describe (указатель на функцию, которая не принимает параметров и ничего не возвращает). Эта структура является чисто интерфейсом — она не хранит данных, только поведение.
  • main.c: Подключите заголовочный файл и реализуйте конкретные функции, которые могут соответствовать интерфейсу:
    • add — возвращает сумму двух целых чисел
    • multiply — возвращает произведение двух целых чисел
    • describe_add — выводит Addition
    • describe_multiply — выводит Multiplication
    Затем создайте экземпляр ICalculator, настройте его в зависимости от входных данных и используйте его для выполнения вычисления.

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

В зависимости от кода операции, свяжите ваш ICalculator с соответствующей парой функций. Затем вызовите describe через интерфейс, чтобы вывести название операции, а затем вызовите operate, чтобы вычислить и вывести результат.

Пример вывода, когда входные данные 5, 3 и 1:

Addition
8

Пример вывода, когда входные данные 4, 7 и 2:

Multiplication
28

Ключевая идея заключается в том, что ваша структура ICalculator определяет контракт — любой код, использующий её, знает, что он может вызвать describe и operate, не зная, какие именно функции подключены. Не забудьте использовать include guards в заголовочном файле.

Шпаргалка

Интерфейс — это структура, которая содержит только указатели на функции, определяя, какие операции доступны, не уточняя, как они работают.

Определение интерфейса:

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

typedef struct {
    LogFunc log;
} ILogger;

Префикс «I» — это общепринятое соглашение, указывающее на то, что это интерфейс. Структура не содержит данных — она описывает исключительно поведение.

Использование интерфейса:

void process(ILogger* logger, const char* msg) {
    logger->log(msg);  // Мы не знаем, КАК происходит логирование
}

Такое разделение обеспечивает полиморфизм — функция работает с любой реализацией интерфейса, позволяя менять поведение без изменения кода самой функции.

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

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

// TODO: Реализовать функцию сложения
// Возвращает сумму двух целых чисел


// TODO: Реализовать функцию умножения
// Возвращает произведение двух целых чисел


// TODO: Реализовать функцию describe_add
// Выводит "Addition"


// TODO: Реализовать функцию describe_multiply
// Выводит "Multiplication"


int main() {
    int num1, num2, op_code;
    scanf("%d", &num1);
    scanf("%d", &num2);
    scanf("%d", &op_code);
    
    // TODO: Создать экземпляр ICalculator
    
    
    // TODO: В зависимости от op_code (1 для сложения, 2 для умножения),
    // подключить к калькулятору соответствующие функции
    
    
    // TODO: Вызвать describe через интерфейс
    
    
    // TODO: Вызвать operate через интерфейс и вывести результат
    
    
    return 0;
}
quiz iconПроверьте себя

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

Все уроки раздела Object Oriented Programming