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.
Desafío
FácilVamos 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 llamadoTaskFuncque no reciba parámetros y no devuelva nada. Luego, define una estructuraTaskque contenga unname(unconst char*) y un puntero a funciónrunde tipoTaskFunc.task.c: Implementa tres funciones de tarea diferentes que representen distintos tipos de trabajo:backup_task— imprimeBacking up data...cleanup_task— imprimeCleaning up files...report_task— imprimeGenerating report...
main.c: Reúne todo aquí. Crea un array de estructurasTask, 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ónrun.
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;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta8Polimorfismo
Punteros a funciones en estructurasSimulando métodosEl concepto de interfazImplementando interfacesIteración polimórficaResumen: Greeter3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculo