Implementando interfaces
Parte de la sección Programación Orientada a Objetos del Journey de C de Coddy. Lección 41 de 61.
Ahora que entiendes qué es una interfaz, una estructura que contiene únicamente punteros a funciones, es hora de crear implementaciones concretas que cumplan ese contrato.
Recuerda 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 y después 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 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 conectada. El código que utiliza estos registradores 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); // Usa console_log
do_work(&file); // Usa file_log
return 0;
}La función do_work está completamente desacoplada de la implementación de registro. Puedes cambiar los registradores libremente sin modificar ninguna de las lógicas principales: ese es el poder de programar hacia una interfaz.
Desafío
FácilConstruyamos un sistema INotifier que demuestre cómo distintas implementaciones pueden cumplir el mismo contrato de interfaz. Crearás notificadores concretos, EmailNotifier y SMSNotifier, que se ajustan a una interfaz común, pero producen salidas diferentes.
Organizarás tu código en tres archivos:
notifier.h: Define aquí la interfaz. Crea un tipo de puntero a función llamadoNotifyFuncque reciba un mensajeconst char*y no devuelva nada. Después, define una estructuraINotifierque contenga únicamente un puntero a funciónnotifyde este tipo. Declara también dos funciones constructoras:create_email_notifierycreate_sms_notifier, cada una de las cuales devuelve unINotifierpor valor.notifier.c: Implementa aquí las funciones de notificación concretas y las funciones constructoras. Crea:email_notify: imprime el mensaje con el formato:[EMAIL] messagesms_notify: imprime el mensaje con el formato:[SMS] messagecreate_email_notifier: devuelve unINotifierconectado aemail_notifycreate_sms_notifier: devuelve unINotifierconectado asms_notify
main.c: Reúne aquí todos los elementos. Lee un tipo de notificación y un mensaje, crea el notificador apropiado mediante 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 correo electrónico, 2 para SMS) y un mensaje que enviar.
Usa las funciones constructoras para crear el notificador apropiado y después llama a notify a través de la interfaz para mostrar el mensaje.
Salida de ejemplo cuando el tipo es 1 y el mensaje es Meeting at 3pm:
[EMAIL] Meeting at 3pmSalida de ejemplo cuando el tipo es 2 y el mensaje es Your code shipped:
[SMS] Your code shippedLa ventaja 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 protectores de inclusión en tu archivo de encabezado.
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: Crea el notificador apropiado según el tipo
// Usa create_email_notifier() para el tipo 1
// Usa create_sms_notifier() para el tipo 2
// TODO: Llama 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 Programación Orientada a Objetos
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 figurasPractica por tu cuenta: Compilador de C online