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 que contém 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 correspondam à 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 a saída para 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 associada. 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 está completamente desacoplada da implementação do registro em log. Você pode trocar os registradores à vontade sem alterar nenhuma parte da lógica principal: 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 structINotifierque contenha apenas um ponteiro para 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: exibe a mensagem no formato:[EMAIL] messagesms_notify: exibe a mensagem no formato:[SMS] messagecreate_email_notifier: retorna umINotifierassociado aemail_notifycreate_sms_notifier: retorna umINotifierassociado 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 pela 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 pela 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 shippedA vantagem 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 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(); // consuma o caractere de nova linha
// Leia a mensagem
fgets(message, sizeof(message), stdin);
// Remova o caractere de nova linha no final, se houver
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Crie o notificador apropriado com base em type
// Use create_email_notifier() para type 1
// Use create_sms_notifier() para type 2
// TODO: Chame a função notify por meio 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çalhoProteções de inclusãoArquivos-fonteFunções estáticasRevisão: calculadora modular4Encapsulamento
Conceito de ponteiros opacosDefinindo structs opacasGetters e settersValidação em settersRevisão: caixa secreta2Objetos e métodos
Structs como objetosO ponteiro 'Self'Correção de constPonteiro vs. valorMétodos auxiliaresRevisão: Gerenciador de pontos5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta8Polimorfismo
Ponteiros para funções em structsSimulando métodosO conceito de interfaceImplementando interfacesIteração polimórficaRevisão: Greeter3Ciclo de vida do objeto
Padrão de construtorPadrão de destrutorInicialização da pilhaCópia profundaRevisão: wrapper de String6Herança por composição
Incorporação de structRegra do primeiro membroAcessando membros do tipo paiUpcastingRevisão: hierarquia de formas9Projeto: Desenhador de Formas
Visão geral do projetoImplementação do círculoImplementação do retânguloUso polimórficoContêiner de formasPratique por conta própria: Compilador de C online