Implementando interfaces
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 41 de 61.
Ahora que entiendes lo que es una interfaz — un struct que contiene solo punteros a funciones — es hora de crear implementaciones concretas que cumplan ese contrato.
Recordemos nuestra interfaz ILogger de la lección anterior:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Para implementar esta interfaz, escribimos funciones reales que coincidan con la firma de LogFunc, luego creamos instancias de ILogger con esas funciones asignadas:
void console_log(const char* message) {
printf("[CONSOLE] %s\n", message);
}
void file_log(const char* message) {
printf("[FILE] %s\n", message); // Simulando la salida a un archivo
}
ILogger create_console_logger() {
ILogger logger = { console_log };
return logger;
}
ILogger create_file_logger() {
ILogger logger = { file_log };
return logger;
}Cada función "constructor" devuelve un ILogger con una función diferente vinculada. El código que utiliza estos loggers no necesita saber qué implementación recibió:
void do_work(ILogger* logger) {
logger->log("Starting work...");
logger->log("Work complete!");
}
int main() {
ILogger console = create_console_logger();
ILogger file = create_file_logger();
do_work(&console); // Utiliza console_log
do_work(&file); // Utiliza file_log
return 0;
}La función do_work está completamente desacoplada de la implementación de registro. Puedes intercambiar los registradores libremente sin cambiar nada de la lógica central; ese es el poder de programar hacia una interfaz.
Desafío
FácilVamos a construir un sistema INotifier que demuestre cómo diferentes implementaciones pueden cumplir con el mismo contrato de interfaz. Crearás notificadores concretos — un EmailNotifier y un SMSNotifier — que cumplen con una interfaz común pero producen salidas diferentes.
Organizarás tu código en tres archivos:
notifier.h: Define la interfaz aquí. Crea un tipo de puntero a función llamadoNotifyFuncque reciba un mensajeconst char*y no devuelva nada. Luego, define una estructuraINotifierque contenga solo un puntero a funciónnotifyde este tipo. También declara dos funciones constructoras:create_email_notifierycreate_sms_notifier, cada una devolviendo unINotifierpor valor.notifier.c: Implementa las funciones de notificación concretas y los constructores aquí. Crea:email_notify— imprime el mensaje en el formato:[EMAIL] messagesms_notify— imprime el mensaje en el formato:[SMS] messagecreate_email_notifier— devuelve unINotifiervinculado aemail_notifycreate_sms_notifier— devuelve unINotifiervinculado asms_notify
main.c: Une todo aquí. Lee un tipo de notificación y un mensaje, crea el notificador apropiado usando las funciones constructoras y envía la notificación a través de la interfaz.
Tu programa recibirá dos entradas: un tipo de notificación (1 para email, 2 para SMS) y un mensaje para enviar.
Usa las funciones constructoras para crear el notificador apropiado, luego llama a notify a través de la interfaz para mostrar el mensaje.
Ejemplo de salida cuando el tipo es 1 y el mensaje es Meeting at 3pm:
[EMAIL] Meeting at 3pmEjemplo de salida cuando el tipo es 2 y el mensaje es Your code shipped:
[SMS] Your code shippedEl poder de este patrón es que main.c no necesita saber cómo funciona internamente cada notificador — simplemente llama a la función notify de la interfaz, y se ejecuta la implementación correcta. Recuerda usar guardas de inclusión en tu archivo de cabecera.
Pruébalo tú mismo
#include <stdio.h>
#include "notifier.h"
int main() {
int type;
char message[256];
// Lee el tipo de notificación (1 para email, 2 para SMS)
scanf("%d", &type);
getchar(); // consume el salto de línea
// Lee el mensaje
fgets(message, sizeof(message), stdin);
// Elimina el salto de línea final si está presente
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Crear el notificador apropiado basado en el tipo
// Usa create_email_notifier() para el tipo 1
// Usa create_sms_notifier() para el tipo 2
// TODO: Llamar a la función notify a través de la interfaz
// notifier.notify(message);
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írculoImplementación del rectánguloUso polimórficoContenedor de figuras