Menu
Coddy logo textTech

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.

challenge icon

Desafío

Fácil

Vamos 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 llamado NotifyFunc que reciba un mensaje const char* y no devuelva nada. Luego, define una estructura INotifier que contenga solo un puntero a función notify de este tipo. También declara dos funciones constructoras: create_email_notifier y create_sms_notifier, cada una devolviendo un INotifier por valor.
  • notifier.c: Implementa las funciones de notificación concretas y los constructores aquí. Crea:
    • email_notify — imprime el mensaje en el formato: [EMAIL] message
    • sms_notify — imprime el mensaje en el formato: [SMS] message
    • create_email_notifier — devuelve un INotifier vinculado a email_notify
    • create_sms_notifier — devuelve un INotifier vinculado a sms_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 3pm

Ejemplo de salida cuando el tipo es 2 y el mensaje es Your code shipped:

[SMS] Your code shipped

El 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;
}
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