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.
Desafio
FácilVamos 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 chamadoNotifyFuncque recebe uma mensagemconst char*e não retorna nada. Em seguida, defina uma structINotifiercontendo apenas um ponteiro de funçãonotifydeste tipo. Também declare duas funções construtoras:create_email_notifierecreate_sms_notifier, cada uma retornando umINotifierpor valor.notifier.c: Implemente as funções de notificação concretas e os construtores aqui. Crie:email_notify— imprime a mensagem no formato:[EMAIL] messagesms_notify— imprime a mensagem no formato:[SMS] messagecreate_email_notifier— retorna umINotifiervinculado aemail_notifycreate_sms_notifier— retorna umINotifiervinculado asms_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 3pmExemplo de saída quando o tipo é 2 e a mensagem é Your code shipped:
[SMS] Your code shippedO 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;
}
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
1Fundamentos da Programação Modular
Arquivos de CabeçalhoInclude GuardsArquivos-FonteFunções EstáticasRecapitulação: Calculadora Modular4Encapsulamento
Conceito de Ponteiros OpacosDefinindo Structs OpacasGetters e SettersValidação em SettersRecapitulação: Caixa Secreta2Objetos e Métodos
Structs como ObjetosO Ponteiro 'Self'Const CorrectnessPonteiro vs ValorMétodos AuxiliaresRecapitulação: Point Manager5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta8Polimorfismo
Ponteiros de Função em StructsSimulando MétodosO Conceito de InterfaceImplementando InterfacesIteração PolimórficaRecapitulação: Greeter3Ciclo de Vida de Objetos
Padrão de ConstrutorPadrão de DestrutorInicialização na StackCópia ProfundaRecapitulação: String Wrapper6Herança via Composição
Incorporação de StructsA Regra do Primeiro MembroAcessando Membros PaiUpcastingRecapitulação: Hierarquia de Formas9Projeto: Desenhador de Formas
Visão Geral do ProjetoImplementação do Círculo