Menu
Coddy logo textTech

Полиморфная итерация

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

Вы научились создавать интерфейсы и implement их с помощью различных конкретных functions. Настоящая польза проявляется, когда вы можете единообразно работать с коллекцией различных объектов: перебирать их и вызывать один и тот же function pointer для каждого из них, независимо от того, какое конкретное поведение подключено.

Представьте, что у вас есть массив структур Action, каждая из которых имеет назначенную ей функцию execute:

typedef void (*ExecuteFunc)(void);

typedef struct {
    const char* name;
    ExecuteFunc execute;
} Action;

void jump(void) { printf("Jumping!\n"); }
void run(void)  { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }

Вы можете создать array из этих actions, each из которых связано с другой function, затем перебрать их с помощью простого цикла for:

int main() {
    Action actions[3] = {
        { "Jump", jump },
        { "Run", run },
        { "Rest", rest }
    };
    
    for (int i = 0; i < 3; i++) {
        printf("%s: ", actions[i].name);
        actions[i].execute();
    }
    return 0;
}

Цикл не знает и не заботится о том, что делает каждое действие внутри. Он просто вызывает execute() для каждого элемента.

Это полиморфизм в действии: один и тот же код обрабатывает объекты с совершенно разным поведением. Добавление нового типа действия не требует изменений в логике итерации; просто добавьте ещё один элемент в array.

challenge icon

Задание

Легко

Давайте создадим систему управления Task, демонстрирующую полиморфную итерацию: обработку коллекции объектов, которые имеют общий интерфейс, но выполняют разные действия.

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

  • task.h: здесь Define интерфейс задачи. Create тип указателя на function с именем TaskFunc, который не принимает parameters и возвращает nothing. Затем Define struct Task, содержащий name (тип const char*) и указатель на function run типа TaskFunc.
  • task.c: Implement три разные function задач, представляющие разные виды работы:
    • backup_task: выводит Backing up data...
    • cleanup_task: выводит Cleaning up files...
    • report_task: выводит Generating report...
  • main.c: соберите здесь всё вместе. Create array из структур Task, каждую из которых свяжите с отдельной function задачи. Затем Iterate по array и Execute каждую задачу полиморфно: для каждой задачи выведите ее имя, а затем вызовите ее function run.

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

Create array, содержащий ровно три задачи в следующем order: backup task с именем Backup, cleanup task с именем Cleanup и report task с именем Report. В зависимости от входного значения count Iterate только по такому числу задач, начиная с начала array.

Для каждой задачи в ходе итерации выведите имя задачи, за которым следуют двоеточие и пробел, затем вызовите ее function run.

Пример вывода при входном значении 2:

Backup: Backing up data...
Cleanup: Cleaning up files...

Пример вывода при входном значении 3:

Backup: Backing up data...
Cleanup: Cleaning up files...
Report: Generating report...

Главная идея заключается в том, что вашему циклу не нужно знать, что делает каждая задача. Он просто выводит имя и вызывает run() для каждого элемента. Один и тот же код итерации единообразно обрабатывает все типы задач. Не забудьте использовать include guards в своем заголовочном файле.

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

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

// Объявить функции задач, реализованные в task.c
void backup_task(void);
void cleanup_task(void);
void report_task(void);

int main() {
    int count;
    scanf("%d", &count);
    
    // TODO: Создайте массив из 3 структур Task в следующем порядке:
    // 1. Задача Backup с именем "Backup", использующая backup_task
    // 2. Задача Cleanup с именем "Cleanup", использующая cleanup_task
    // 3. Задача Report с именем "Report", использующая report_task
    
    // TODO: Пройдите по 'count' задачам из массива
    // Для каждой задачи выведите: "<name>: ", затем вызовите её функцию run
    
    return 0;
}
quiz iconПроверьте себя

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

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

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