Implementando Interfaces
Parte da seção Programação Orientada a Objetos do Journey de C da Coddy. Lição 41 de 61.
Agora que você entende o que é uma interface, uma struct contendo apenas ponteiros para funções, é 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 essa interface, escrevemos funções reais que correspondem à assinatura de LogFunc e, em seguida, 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 "construtora" retorna um ILogger com uma função diferente conectada. O código que usa esses registradores 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 é completamente desacoplada da implementação de registro. Você pode trocar os registradores livremente sem alterar nenhuma lógica central: esse é o poder de programar para uma interface.
Desafio
FácilVamos criar 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 seguem 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 para função chamadoNotifyFuncque recebe uma mensagemconst char*e não retorna nada. Em seguida, defina uma structINotifiercontendo apenas um ponteiro de funçãonotifydesse tipo. Declare também duas funções construtoras:create_email_notifierecreate_sms_notifier, cada uma retornando umINotifierpor valor.notifier.c: Implemente aqui as funções concretas de notificação e as funções construtoras. Crie:email_notify: imprime a mensagem no formato:[EMAIL] messagesms_notify: imprime a mensagem no formato:[SMS] messagecreate_email_notifier: retorna umINotifierconectado aemail_notifycreate_sms_notifier: retorna umINotifierconectado 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 por meio da interface.
Seu programa receberá duas entradas: um tipo de notificação (1 para e-mail, 2 para SMS) e uma mensagem a ser enviada.
Use as funções construtoras para criar o notificador apropriado e, em seguida, chame notify por meio 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 desse padrão é que 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 guardas de inclusão no seu arquivo de cabeçalho.
Experimente você mesmo
#include <stdio.h>
#include "notifier.h"
int main() {
int type;
char message[256];
// Leia o tipo de notificação (1 para email, 2 para SMS)
scanf("%d", &type);
getchar(); // consome a nova linha
// Leia 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: Crie o notifier apropriado com base no tipo
// Use create_email_notifier() para o tipo 1
// Use create_sms_notifier() para o tipo 2
// TODO: Chame 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 Programação Orientada a Objetos
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írculoImplementação do RetânguloUso PolimórficoContainer de FormasPratique por conta própria: Compilador de C online