Menu
Coddy logo textTech

Implementando Interfaces

Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 41 de 61.

Agora que você entende o que é uma interface — uma struct contendo apenas ponteiros de função — é hora de criar implementações concretas que cumpram esse contrato.

Relembre nossa interface ILogger da lição anterior:

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

Para implementar esta interface, escrevemos funções reais que correspondam à assinatura LogFunc, e então criamos instâncias de ILogger com essas funções atribuídas:

void console_log(const char* message) {
    printf("[CONSOLE] %s\n", message);
}

void file_log(const char* message) {
    printf("[FILE] %s\n", message);  // Simulando saída de arquivo
}

ILogger create_console_logger() {
    ILogger logger = { console_log };
    return logger;
}

ILogger create_file_logger() {
    ILogger logger = { file_log };
    return logger;
}

Cada função "constructor" retorna um ILogger com uma função diferente vinculada. O código que usa esses loggers não precisa saber qual implementação recebeu:

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

A função do_work está completamente desacoplada da implementação de logging. Você pode trocar os loggers livremente sem alterar nada da lógica principal — esse é o poder de programar para uma interface.

challenge icon

Desafio

Fácil

Vamos construir um sistema INotifier que demonstra como diferentes implementações podem cumprir o mesmo contrato de interface. Você criará notificadores concretos — um EmailNotifier e um SMSNotifier — que ambos se conformam a uma interface comum, mas produzem saídas diferentes.

Você organizará seu código em três arquivos:

  • notifier.h: Defina a interface aqui. Crie um tipo de ponteiro de função chamado NotifyFunc que recebe uma mensagem const char* e não retorna nada. Em seguida, defina uma struct INotifier contendo apenas um ponteiro de função notify deste tipo. Também declare duas funções construtoras: create_email_notifier e create_sms_notifier, cada uma retornando um INotifier por valor.
  • notifier.c: Implemente as funções de notificação concretas e os construtores aqui. Crie:
    • email_notify — imprime a mensagem no formato: [EMAIL] message
    • sms_notify — imprime a mensagem no formato: [SMS] message
    • create_email_notifier — retorna um INotifier vinculado a email_notify
    • create_sms_notifier — retorna um INotifier vinculado a sms_notify
  • main.c: Reúna tudo aqui. Leia um tipo de notificação e uma mensagem, crie o notificador apropriado usando as funções construtoras e envie a notificação através da interface.

Seu programa receberá duas entradas: um tipo de notificação (1 para e-mail, 2 para SMS) e uma mensagem para enviar.

Use as funções construtoras para criar o notificador apropriado e, em seguida, chame notify através da interface para exibir a mensagem.

Exemplo de saída quando o tipo é 1 e a mensagem é Meeting at 3pm:

[EMAIL] Meeting at 3pm

Exemplo de saída quando o tipo é 2 e a mensagem é Your code shipped:

[SMS] Your code shipped

O poder deste padrão é que o main.c não precisa saber como cada notificador funciona internamente — ele simplesmente chama a função notify da interface, e a implementação correta é executada. Lembre-se de usar include guards em seu arquivo de cabeçalho.

Folha de consulta

Para implementar uma interface em C, crie funções concretas que correspondam à assinatura do ponteiro de função e, em seguida, atribua-as às instâncias da interface.

Defina a interface com ponteiros de função:

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

Crie implementações concretas:

void console_log(const char* message) {
    printf("[CONSOLE] %s\n", message);
}

void file_log(const char* message) {
    printf("[FILE] %s\n", message);
}

Use funções construtoras para criar instâncias da interface:

ILogger create_console_logger() {
    ILogger logger = { console_log };
    return logger;
}

ILogger create_file_logger() {
    ILogger logger = { file_log };
    return logger;
}

O código que utiliza a interface é desacoplado de implementações específicas:

void do_work(ILogger* logger) {
    logger->log("Starting work...");
}

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

Experimente você mesmo

#include <stdio.h>
#include "notifier.h"

int main() {
    int type;
    char message[256];
    
    // Lê o tipo de notificação (1 para email, 2 para SMS)
    scanf("%d", &type);
    getchar(); // consome a nova linha
    
    // Lê a mensagem
    fgets(message, sizeof(message), stdin);
    // Remove a nova linha final, se presente
    int len = 0;
    while (message[len] != '\0') len++;
    if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
    
    // TODO: Criar o notifier apropriado com base no tipo
    // Use create_email_notifier() para o tipo 1
    // Use create_sms_notifier() para o tipo 2
    
    // TODO: Chamar a função notify através da interface
    // notifier.notify(message);
    
    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming