El concepto de interfaz
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 40 de 61.
Hasta ahora, nuestros structs han mezclado datos y comportamiento — un nombre junto a un puntero de función, por ejemplo. ¿Pero qué pasaría si un struct contuviera solo punteros de función? Esto crea algo poderoso: una interfaz.
Una interfaz define qué operaciones están disponibles sin especificar cómo funcionan. Es un contrato que dice "cualquier objeto que implemente esta interfaz debe proporcionar estas funciones".
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;La estructura ILogger no contiene nada más que un puntero a función. No tiene datos propios; describe puramente el comportamiento. El prefijo «I» es una convención común que indica que se trata de una interfaz.
Cualquier código que acepte un ILogger no le importa a dónde va el mensaje de registro. Podría imprimir en la consola, escribir en un archivo o enviar datos a través de una red. El llamador simplemente sabe que puede llamar a log:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // No sabemos CÓMO registra
}Esta separación es la esencia del polimorfismo. La función process funciona con cualquier implementación de logger — puedes intercambiar comportamientos sin cambiar el código de la función. En la siguiente lección, crearás implementaciones concretas que cumplan con este contrato de interfaz.
Desafío
FácilVamos a construir una interfaz ICalculator — una estructura que contiene solo punteros a funciones, definiendo qué operaciones debe soportar cualquier calculadora sin especificar cómo funcionan.
Crearás dos archivos para organizar tu código:
calculator.h: Define tu interfaz aquí. Crea un tipo de puntero a función llamadoBinaryOpque reciba dos parámetrosinty devuelva unint. Luego, define una estructuraICalculatorque contenga solo dos punteros a funciones:operate(de tipoBinaryOp) ydescribe(un puntero a función que no recibe parámetros y no devuelve nada). Esta estructura es puramente una interfaz — no contiene datos, solo comportamiento.main.c: Incluye tu cabecera e implementa funciones concretas que puedan cumplir con la interfaz:add— devuelve la suma de dos números enterosmultiply— devuelve el producto de dos números enterosdescribe_add— imprimeAdditiondescribe_multiply— imprimeMultiplication
ICalculator, conéctala según la entrada y úsala para realizar un cálculo.
Tu programa recibirá tres entradas: dos enteros y un código de operación (1 para suma, 2 para multiplicación).
Basándote en el código de operación, conecta tu ICalculator con el par de funciones apropiado. Luego, llama a describe a través de la interfaz para imprimir el nombre de la operación, seguido de una llamada a operate para calcular e imprimir el resultado.
Ejemplo de salida cuando las entradas son 5, 3 y 1:
Addition
8Ejemplo de salida cuando las entradas son 4, 7 y 2:
Multiplication
28La idea clave es que tu estructura ICalculator define un contrato — cualquier código que la use sabe que puede llamar a describe y operate, sin saber qué funciones específicas están conectadas. Recuerda usar guardas de inclusión en tu archivo de cabecera.
Hoja de referencia
Una interfaz es una estructura que contiene solo punteros a funciones, definiendo qué operaciones están disponibles sin especificar cómo funcionan.
Definiendo una interfaz:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;El prefijo "I" es una convención común que indica que se trata de una interfaz. La estructura no contiene datos; puramente describe el comportamiento.
Usando una interfaz:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // No sabemos CÓMO registra
}Esta separación permite el polimorfismo: la función trabaja con cualquier implementación de la interfaz, lo que le permite intercambiar comportamientos sin cambiar el código de la función.
Pruébalo tú mismo
#include <stdio.h>
#include "calculator.h"
// TODO: Implementar la función add
// Devuelve la suma de dos enteros
// TODO: Implementar la función multiply
// Devuelve el producto de dos enteros
// TODO: Implementar la función describe_add
// Imprime "Addition"
// TODO: Implementar la función describe_multiply
// Imprime "Multiplication"
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO: Crear una instancia de ICalculator
// TODO: Basándose en op_code (1 para add, 2 para multiply),
// conectar la calculadora con las funciones apropiadas
// TODO: Llamar a describe a través de la interfaz
// TODO: Llamar a operate a través de la interfaz e imprimir el resultado
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