Interfaces e classes abstratas
ResumoUma interface é um contrato puro (o "o quê"), sem estado. Uma classe abstrata é uma base parcial (o "o quê" + parte do "como"). Programar contra abstrações é a base da injeção de dependência e da testabilidade.
1. Objetivos de Aprendizagem#
- Declarar e implementar interfaces.
- Diferenciar interface de classe abstrata.
- Entender por que "programar para interfaces" habilita DI e testes.
2. Pré-requisitos#
- Herança e polimorfismo (tópico 5.2).
3. Conceito#
- Interface (
interface IX): só declara membros (métodos, propriedades). Uma classe pode implementar várias interfaces. Não tem estado. - Classe abstrata (
abstract class): pode ter estado e implementação parcial, com membrosabstractque as derivadas devem completar. Herança única.
O porquê: depender de IRepositorioProduto (interface) em vez de uma implementação concreta permite trocar a implementação (SQL, memória, mock de teste) sem mudar quem consome. É o alicerce da injeção de dependência (Módulo 12).
4. Mão na Massa#
4.1. Setup#
dotnet new console -n Catalog.Contratos
cd Catalog.Contratos
4.2. Implementação Passo a Passo#
- Interface (contrato):
public interface INotificador
{
void Enviar(string destino, string mensagem);
}
- Implementações intercambiáveis:
public class NotificadorEmail : INotificador
{
public void Enviar(string destino, string mensagem) =>
Console.WriteLine($"[email->{destino}] {mensagem}");
}
public class NotificadorFake : INotificador // útil em testes
{
public void Enviar(string destino, string mensagem) { /* no-op */ }
}
- Consumidor depende do contrato, não da implementação:
public class ServicoPedido
{
private readonly INotificador _notificador;
public ServicoPedido(INotificador notificador) => _notificador = notificador;
public void Confirmar(string email) =>
_notificador.Enviar(email, "Pedido confirmado!");
}
new ServicoPedido(new NotificadorEmail()).Confirmar("ana@x.com");
4.3. Executando#
dotnet run
5. Exemplo Completo#
ServicoPedido funciona com qualquer INotificador. Em produção você injeta NotificadorEmail; em teste, NotificadorFake — sem alterar o serviço.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
| Depender de interfaces nas fronteiras (serviços, repositórios) | Instanciar dependências concretas com new dentro da classe |
| Interfaces pequenas e focadas (ISP) | Interfaces "gigantes" com dezenas de membros |
| Classe abstrata quando há estado/implementação comum | Interface quando você precisa compartilhar código concreto |
Notainterfaces podem ter membros com implementação padrão (default interface methods), mas use isso com parcimônia — na maioria dos casos, o contrato puro é mais claro.
7. Segurança e Produção#
- Depender de abstrações facilita substituir implementações inseguras/legadas e escrever testes que não tocam recursos externos (rede, banco).
8. Exercícios#
- Fácil: crie
IArmazenamentocomSalvar(string). - Médio: implemente duas versões (arquivo e memória).
- Desafio: faça um serviço que dependa de
IArmazenamentoe teste-o com a versão em memória.
9. Resumo#
Interface = contrato puro (herança múltipla); classe abstrata = base parcial com estado (herança única). Programar contra interfaces habilita DI e testes.
10. Próximos Passos#
Encerramos OOP. A seguir, recursos de C# moderno que deixam o código mais expressivo.
11. Referências#
- Microsoft Learn — Interfaces
- Microsoft Learn — Classes e membros abstratos
- Microsoft Learn — Interface vs classe abstrata