O Conceito de Interface
Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 40 de 61.
Até agora, nossas structs misturaram dados e comportamento — um nome ao lado de um ponteiro de função, por exemplo. Mas e se uma struct contivesse apenas ponteiros de função? Isso cria algo poderoso: uma interface.
Uma interface define quais operações estão disponíveis sem especificar como elas funcionam. É um contrato que diz "qualquer objeto que implemente esta interface deve fornecer estas funções".
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;A struct ILogger não contém nada além de um ponteiro de função. Ela não possui dados próprios — ela puramente descreve comportamento. O prefixo "I" é uma convenção comum indicando que se trata de uma interface.
Qualquer código que aceite um ILogger não se importa para onde a mensagem de log vai. Ele poderia imprimir no console, escrever em um arquivo ou enviar dados por uma rede. O chamador apenas sabe que pode chamar log:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // Não sabemos COMO ele faz o log
}Essa separação é a essência do polimorfismo. A função process funciona com qualquer implementação de logger — você pode trocar comportamentos sem alterar o código da função. Na próxima lição, você criará implementações concretas que cumprem este contrato de interface.
Desafio
FácilVamos construir uma interface ICalculator — uma struct que contém apenas ponteiros de função, definindo quais operações qualquer calculadora deve suportar sem especificar como elas funcionam.
Você criará dois arquivos para organizar seu código:
calculator.h: Defina sua interface aqui. Crie um tipo de ponteiro de função chamadoBinaryOpque recebe dois parâmetrosinte retorna umint. Em seguida, defina uma structICalculatorque contenha apenas dois ponteiros de função:operate(do tipoBinaryOp) edescribe(um ponteiro de função que não recebe parâmetros e não retorna nada). Esta struct é puramente uma interface — ela não contém dados, apenas comportamento.main.c: Inclua seu cabeçalho e implemente funções concretas que possam cumprir a interface:add— retorna a soma de dois inteirosmultiply— retorna o produto de dois inteirosdescribe_add— imprimeAdditiondescribe_multiply— imprimeMultiplication
ICalculator, conecte-a com base na entrada e use-a para realizar um cálculo.
Seu programa receberá três entradas: dois inteiros e um código de operação (1 para adição, 2 para multiplicação).
Com base no código de operação, conecte seu ICalculator com o par apropriado de funções. Em seguida, chame describe através da interface para imprimir o nome da operação, seguido pela chamada de operate para calcular e imprimir o resultado.
Exemplo de saída quando as entradas são 5, 3 e 1:
Addition
8Exemplo de saída quando as entradas são 4, 7 e 2:
Multiplication
28O ponto principal é que sua struct ICalculator define um contrato — qualquer código que a utilize sabe que pode chamar describe e operate, sem saber quais funções específicas estão conectadas. Lembre-se de usar guards de inclusão em seu arquivo de cabeçalho.
Folha de consulta
Uma interface é uma struct que contém apenas ponteiros de função, definindo quais operações estão disponíveis sem especificar como elas funcionam.
Definindo uma interface:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;O prefixo "I" é uma convenção comum indicando que se trata de uma interface. A struct não contém dados — ela descreve puramente o comportamento.
Usando uma interface:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // Nós não sabemos COMO ele registra o log
}Esta separação permite o polimorfismo — a função trabalha com qualquer implementação da interface, permitindo que você troque comportamentos sem alterar o código da função.
Experimente você mesmo
#include <stdio.h>
#include "calculator.h"
// TODO: Implementar a função add
// Retorna a soma de dois inteiros
// TODO: Implementar a função multiply
// Retorna o produto de dois inteiros
// TODO: Implementar a função describe_add
// Imprime "Addition"
// TODO: Implementar a função describe_multiply
// Imprime "Multiplication"
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO: Criar uma instância de ICalculator
// TODO: Com base no op_code (1 para add, 2 para multiply),
// conecte a calculadora com as funções apropriadas
// TODO: Chamar describe através da interface
// TODO: Chamar operate através da interface e imprimir o resultado
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