Menu
Coddy logo textTech

Iteración polimórfica

Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 42 de 61.

Has aprendido a crear interfaces e implementarlas con diferentes funciones concretas. La verdadera recompensa llega cuando puedes tratar una colección de diferentes objetos de manera uniforme — iterando a través de ellos y llamando al mismo puntero de función en cada uno, independientemente del comportamiento específico que esté conectado.

Imagina que tienes un array de structs Action, cada uno con su propia función execute asignada:

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

Puedes crear un array de estas acciones, cada una vinculada a una función diferente, y luego recorrerlas con un simple bucle 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;
}

El bucle no sabe ni le importa lo que cada acción hace internamente. Simplemente llama a execute() en cada elemento.

Esto es polimorfismo en acción: el mismo código maneja objetos con comportamientos completamente diferentes. Agregar un nuevo tipo de acción no requiere cambios en la lógica de iteración; simplemente agregas otro elemento al array.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de gestión de Task que demuestre la iteración polimórfica: procesar una colección de objetos que comparten una interfaz común pero ejecutan comportamientos diferentes.

Organizarás tu código en tres archivos:

  • task.h: Define aquí tu interfaz de tarea. Crea un tipo de puntero a función llamado TaskFunc que no reciba parámetros y no devuelva nada. Luego, define una estructura Task que contenga un name (un const char*) y un puntero a función run de tipo TaskFunc.
  • task.c: Implementa tres funciones de tarea diferentes que representen distintos tipos de trabajo:
    • backup_task — imprime Backing up data...
    • cleanup_task — imprime Cleaning up files...
    • report_task — imprime Generating report...
  • main.c: Reúne todo aquí. Crea un array de estructuras Task, cada una vinculada a una función de tarea diferente. Luego, itera a través del array y ejecuta cada tarea de forma polimórfica; para cada tarea, imprime su nombre seguido de la llamada a su función run.

Tu programa recibirá una única entrada: el número de tareas a ejecutar (1, 2 o 3).

Crea un array que contenga exactamente tres tareas en este orden: una tarea de respaldo llamada Backup, una tarea de limpieza llamada Cleanup y una tarea de informe llamada Report. Basándote en el número de entrada, itera solo esa cantidad de tareas desde el inicio del array.

Para cada tarea en tu iteración, imprime el nombre de la tarea seguido de dos puntos y un espacio, luego llama a su función run.

Ejemplo de salida cuando la entrada es 2:

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

Ejemplo de salida cuando la entrada es 3:

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

La idea clave es que tu bucle no necesita saber qué hace cada tarea; simplemente imprime el nombre y llama a run() en cada elemento. El mismo código de iteración maneja todos los tipos de tareas de manera uniforme. Recuerda usar guardas de inclusión en tu archivo de cabecera.

Hoja de referencia

Puedes iterar a través de colecciones de objetos que comparten una interfaz común, llamando al mismo puntero de función en cada elemento independientemente de su implementación específica. Esto es polimorfismo: tratar diferentes objetos de manera uniforme.

Define un tipo de puntero de función y una estructura que lo utilice:

typedef void (*ExecuteFunc)(void);

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

Crea funciones concretas que coincidan con la firma:

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

Construye un array de estructuras, cada una vinculada a una función diferente, y luego itera a través de ellas:

Action actions[3] = {
    { "Jump", jump },
    { "Run", run },
    { "Rest", rest }
};

for (int i = 0; i < 3; i++) {
    printf("%s: ", actions[i].name);
    actions[i].execute();
}

El bucle no necesita saber qué hace cada acción internamente; simplemente llama al puntero de función en cada elemento. Añadir nuevos tipos no requiere cambios en la lógica de iteración.

Pruébalo tú mismo

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

// Declara las funciones de tarea implementadas en task.c
void backup_task(void);
void cleanup_task(void);
void report_task(void);

int main() {
    int count;
    scanf("%d", &count);
    
    // TODO: Crea un arreglo de 3 structs Task en este orden:
    // 1. Tarea de respaldo llamada "Backup" usando backup_task
    // 2. Tarea de limpieza llamada "Cleanup" usando cleanup_task
    // 3. Tarea de reporte llamada "Report" usando report_task
    
    // TODO: Itera a través de 'count' tareas del arreglo
    // Para cada tarea, imprime: "<name>: " luego llama a su función run
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming