1. O que é uma interface?
Se uma classe abstrata é tipo um "pré-pronto" com implementação parcial, interface é só uma lista de exigências: o que deve saber fazer tal criatura (classe) pra poder ser usada num certo contexto abstrato.
Na programação, interface é tipo um "contrato" ou "lista de exigências" pro comportamento de um objeto. Ela descreve um conjunto de métodos públicos, propriedades, indexadores e eventos que a classe que implementa essa interface tem que fornecer. A interface fala: "Se tu quer ser chamado, por exemplo, de IDriveable (capaz de dirigir), então faz favor de implementar os métodos Drive() e Stop()".
Dá pra imaginar interface como uma Lista de Exigências pra Funcionário: tipo, se tu quer contratar "Cozinheiros", na interface vai estar escrito: "Tem que saber cozinhar, servir pratos, lavar as mãos". Como cada cozinheiro vai fazer isso — é problema dele. O importante é que, por fora, ele cumpra o contrato.
Importante: interface só descreve o que a classe tem que fornecer, não como isso é feito.
Resumindo em termos de OOP
- Interface descreve a "cara" (API) do objeto: que ações ele permite fazer.
- Não tem estado (nada de campos). No conceito clássico, interfaces também não têm implementação, mas nas versões modernas do C# já rolam exceções (tipo métodos default), que a gente vai ver depois.
- Uma classe pode implementar quantas interfaces quiser (diferente de herança de classes!).
Analogias da vida real
Porta USB — todo mundo sabe que dá pra plugar mouse, teclado, pendrive, fone, até cafeteira (sim, existe!). O que tem dentro do "mouse" — tanto faz, só precisa ter o encaixe USB e o dispositivo falar o protocolo certo. Isso é interface!
Pra que servem interfaces?
- Deixa o código menos acoplado. O código trabalha com a interface, não com a classe concreta. A gente programa "no nível da interface".
- Permite implementar herança múltipla de comportamento: uma classe pode implementar várias interfaces.
- Padronização: dá pra fazer mecanismos universais: tipo, todo objeto que pode ser comparado implementa a interface IComparable.
- Testabilidade: fácil trocar a implementação real por um "mock" nos testes.
- Plugabilidade: dá pra adicionar novos "módulos" sem mexer no código que já existe.
2. Sintaxe de declaração de interface
Agora, bora pro código! Em C#, interface se declara com a palavra-chave interface, e o nome da interface geralmente começa com I :
// Declaração de interface
public interface IPrintable
{
// Método abstrato — do contrato
void Print();
// Também dá pra declarar propriedades
string Name { get; set; }
}
Grava aí: métodos e propriedades da interface NÃO têm implementação (não tem corpo do método, só a assinatura — igualzinho método abstrato).
Pontos chave da interface
- Não pode declarar campos (variáveis-membro) dentro da interface.
- Todos os métodos, propriedades, eventos e indexadores por padrão são public (e devem ser assim na implementação).
- Interface não pode ter construtores (afinal, ela não tem estado).
- Interface não depende se a classe que implementa é abstrata ou concreta.
Interface na prática
Bora integrar interface no nosso app de estudos. Agora temos uma interface "imprimível" (IPrintable), que é implementada pela classe Report e, por exemplo, por uma nova classe Invoice.
public interface IPrintable
{
void Print();
string Name { get; set; }
}
Agora vamos definir uma classe que implementa essa interface:
public class Report : IPrintable
{
public string Name { get; set; }
public Report(string name)
{
Name = name;
}
// Implementação do método da interface
public void Print()
{
Console.WriteLine($"Imprimindo relatório: {Name}");
}
}
Agora uma classe totalmente diferente, mas com a mesma interface:
public class Invoice : IPrintable
{
public string Name { get; set; }
public Invoice(string name)
{
Name = name;
}
public void Print()
{
Console.WriteLine($"Imprimindo fatura: {Name}");
}
}
Agora dá pra criar um método que trabalha com qualquer coisa imprimível:
public static void PrintAnything(IPrintable printable)
{
printable.Print(); // Pronto! Não importa se é relatório ou fatura — o importante é saber imprimir.
}
Exemplo de uso:
var report = new Report("Relatório Mensal");
var invoice = new Invoice("Fatura #12345");
PrintAnything(report); // Imprimindo relatório: Relatório Mensal
PrintAnything(invoice); // Imprimindo fatura: Fatura #12345
Assim que as interfaces deixam o código universal, expansível e bonito.
3. Implementação de interfaces
Ligando interface à classe
Interface é implementada pela classe usando dois pontos (igual herança mesmo):
public class Ticket : IPrintable
{
public string Name { get; set; }
public void Print()
{
Console.WriteLine($"Imprimindo ingresso: {Name}");
}
}
Importante: a classe tem que implementar todos os membros da interface. E os membros implementados têm que ser public.
Se esquecer de implementar algum membro:
public class BrokenTicket : IPrintable
{
// Faltou implementar Print()
public string Name { get; set; }
}
// Erro de compilação: 'BrokenTicket' não implementa o membro da interface 'IPrintable.Print()'
Várias interfaces
A classe pode implementar várias interfaces separadas por vírgula:
public interface IStorable
{
void Store();
}
public class MultiPurposeDoc : IPrintable, IStorable
{
public string Name { get; set; }
public void Print()
{
Console.WriteLine("Imprimindo documento");
}
public void Store()
{
Console.WriteLine("Salvando documento");
}
}
4. Pra que servem interfaces?
Talvez agora tu esteja pensando: "Beleza, entendi a sintaxe. Mas pra que serve isso na vida real, além de complicar minha vida nesse curso?" Calma aí! Interface é uma das ferramentas mais usadas no desenvolvimento profissional.
Separação de responsabilidade e baixo acoplamento (Decoupling / Loose Coupling):
- Imagina que tu tá criando um player de música. Não importa de onde vem a música — arquivo local, internet ou CD. O que importa é que a fonte de música consiga fornecer um stream de áudio.
- Tu pode definir a interface IAudioSource com o método GetAudioStream().
- Daí tu vai ter classes FileAudioSource, InternetAudioSource, CDAudioSource, todas implementando essa interface.
- Teu player vai trabalhar com IAudioSource, sem saber o tipo concreto. Se amanhã surgir uma nova fonte, tipo BluetoothAudioSource, tu não precisa mudar o código do player! Só cria uma nova classe implementando IAudioSource. Isso deixa teu sistema muito mais flexível e fácil de expandir. Isso é baixo acoplamento — os componentes dependem de abstrações (interfaces), não de implementações concretas.
Polimorfismo e tratamento uniforme:
Como vimos no exemplo do PrintAnything, tu pode ter vários objetos de tipos diferentes, mas que têm um comportamento comum, descrito na interface. Dá pra chamar o mesmo método (Print()) em todos eles, sem saber quem é — relatório, fatura ou ingresso. Isso deixa o código bem enxuto e universal.
Testes (Unit Testing):
Talvez o uso mais importante das interfaces. Quando tu testa um componente do sistema, ele geralmente depende de outros componentes (tipo, uma classe que salva dados pode depender de uma que mexe com banco de dados).
Ao invés de passar a classe real DatabaseSaver (que precisa de um banco de verdade pra testar!), tu pode passar um objeto "fake" (ou "mockado") que só implementa a interface IDataSaver. Esse "mock" só finge que salva, sem acessar banco nenhum. Assim tu testa os componentes isolados, rápido e sem depender de nada externo.
Desenvolvimento de API e frameworks:
Quando tu cria uma biblioteca ou framework, tu quer dar "pontos de extensão" pros devs. Interface é perfeita pra isso. Tu pode falar: "Se quiser que teu componente funcione no meu sistema, implementa essa interface aqui". As libs padrão do .NET estão cheias de interfaces (tipo IEnumerable<T>, IDisposable, IComparable<T>) — elas definem contratos pros cenários mais comuns.
Programação direto no nível da interface (Programming to an Interface):
Dev experiente sempre fala: "Programa no nível da interface, não da implementação". Isso quer dizer que, quando tu define o tipo de uma variável ou parâmetro de método, em vez de usar a classe concreta (Car), usa a interface (IDriveable). Assim teu código fica mais flexível e menos dependente dos detalhes, podendo trocar uma implementação por outra fácil.
5. Erros comuns ao trabalhar com interfaces
Erro #1: tentar criar uma instância da interface.
Tu pode escrever Cat murzik = new Cat("Murzik", 3);, porque Cat é uma classe concreta. Mas tu não pode escrever ITalkable talker = new ITalkable();. Interface é só contrato, modelo. Não tem implementação e não pode ser criada direto. É tipo uma planta, não uma casa pronta.
Erro #2: esquecer de implementar todos os membros da interface.
Se tu disse que tua classe implementa a interface, tipo IMyInterface, então tem que implementar todos os métodos dela. Se faltar um, vai dar erro de compilação: MyClass não implementa IMyInterface.TheMissingMethod().
Erro #3: modificadores de acesso errados na implementação.
Métodos da interface são implicitamente public, e na implementação também tem que ser public. Se tentar fazer private ou protected, o compilador reclama. Prometeu — implementa aberto.
Erro #4: tentar adicionar campos ou construtores na interface.
Interface descreve comportamento, não estado. Então não pode botar campos nem construtores nela. Se tentar — erro de compilação. Só pode propriedades, e mesmo assim — só a descrição dos getters/setters.
Erro #5: confundir override com implementação de interface.
A palavra override serve pra sobrescrever métodos da classe base. Mas pra implementar interface não precisa — só escreve o método public com a assinatura certa. É um detalhe importante que muita gente esquece.
GO TO FULL VERSION