CS Coder
Delphi

Interfaces na prática: um contrato pequeno para uma dependência real

Implemente um provedor de saudação e entenda contratos, substituição e o cuidado com o ciclo de vida de TInterfacedObject.

· 4 min de leitura
Objetivo e formato

Consumir uma implementação por interface sem misturar referências e destruição manual

Tutorial · prática orientada · Intermediário
Antes de experimentar

Classes, métodos e units

Unit principal compilada e exercitada com DCC32 37.0, build 37.0.59082.6021. Referência editorial: Delphi 11.3 e 13.2; essas duas versões não foram verificadas individualmente.

Documentação, instalação e limites de Fundamentos em seis units

Uma interface é útil quando um consumidor precisa de um comportamento e não precisa conhecer os detalhes de quem o executa. Isso não significa criar uma interface para cada classe. O contrato deve representar uma fronteira que faça sentido no projeto.

Vamos usar uma dependência simples: um objeto que fornece uma saudação. O exemplo evita banco de dados e rede para concentrar a atenção no contrato e no ciclo de vida.

Declare o comportamento necessário

Crie a unit Exemplo.Saudacao.pas. O consumidor receberá ISaudacao, e a implementação concreta ficará em TSaudacaoSimples.

delphi
unit Exemplo.Saudacao;

interface

type
  ISaudacao = interface
    ['{E3C2842B-67ED-4BE2-AB9A-9A715BAE8E10}']
    function Texto(const Nome: string): string;
  end;

  TSaudacaoSimples = class(TInterfacedObject, ISaudacao)
  public
    function Texto(const Nome: string): string;
  end;

function Cumprimentar(const Provedor: ISaudacao;
  const Nome: string): string;

implementation

uses
  System.SysUtils;

function TSaudacaoSimples.Texto(const Nome: string): string;
begin
  Result := 'Olá, ' + Trim(Nome) + '!';
end;

function Cumprimentar(const Provedor: ISaudacao;
  const Nome: string): string;
begin
  if not Assigned(Provedor) then
    raise EArgumentException.Create('Provedor não informado');
  Result := Provedor.Texto(Nome);
end;

end.

A função Cumprimentar não cria a dependência. Ela recebe uma implementação já escolhida. Isso permite alterar a saudação sem modificar o consumidor.

Consuma por uma referência de interface

No projeto de console, declare uma variável Provedor: ISaudacao e utilize:

delphi
Provedor := TSaudacaoSimples.Create;
Writeln(Cumprimentar(Provedor, 'Ana'));
Provedor := nil;

A implementação padrão de TInterfacedObject utiliza contagem de referências. Nesse modelo, o objeto é destruído quando sua última referência de interface é liberada. Isso não é uma propriedade universal de toda classe que implementa uma interface: existem implementações com outro modelo de duração. Veja Interface References.

Não chame Free no objeto enquanto referências gerenciadas de interface continuam usando-o. Também não assuma que um componente com proprietário segue automaticamente o mesmo modelo de TInterfacedObject.

O contrato não elimina decisões

O que deve acontecer quando Nome está vazio? Nesta implementação, a saudação fica sem nome. Isso precisa ser discutido pelo contrato: talvez o consumidor deva exigir uma entrada válida ou o provedor deva oferecer uma saudação genérica.

Uma interface organiza métodos; ela não documenta sozinha regras, falhas, concorrência ou propriedade dos objetos devolvidos. Registre esses comportamentos quando forem relevantes.

Mantenha também a direção da dependência: o provedor não deve precisar de um formulário para montar o texto. A apresentação pode exibir o resultado do contrato em qualquer controle.

Substitua antes de abstrair mais

Crie TSaudacaoFormal, também implementando ISaudacao, e faça Texto retornar outra mensagem. Passe essa implementação para a mesma função Cumprimentar. Se você precisou alterar a assinatura da função, o contrato talvez estivesse acoplado à primeira implementação.

Em um sistema real, contratos semelhantes podem representar envio de notificações ou leitura de dados. Não generalize a interface de saudação para esses problemas: modele cada necessidade com as entradas e os resultados apropriados.

Erros que merecem atenção

Referências de interface que apontam umas para as outras podem formar ciclos e impedir que a contagem chegue a zero. Planeje a direção das referências em vez de esperar que a contagem resolva todos os grafos de objetos.

Outro erro é devolver objetos cuja duração não está clara. Se um método entrega uma instância para o chamador, documente quem é responsável por liberá-la.

Verifique o exemplo e avance

Compile a unit e teste saudação simples, formal e provedor nil. A saída esperada de nil é uma EArgumentException; os demais resultados dependem da implementação escolhida. A unit completa foi compilada; os casos da saudação e do provedor ausente passaram no projeto de verificações.

Continue em Separando tela, serviço e domínio para colocar contratos em uma fronteira de aplicação.

Segunda implementação, mesmo consumidor

  1. Abra a variante TSaudacaoFormal na revisão dos fundamentos.
  2. Compare a mesma assinatura ISaudacao com TSaudacaoSimples.
  3. Passe a variante a Cumprimentar sem alterar a chamada.
  4. Confira ausência de provedor e resultado da saudação formal no runner.

Criação → referência ISaudacao → consumidor → última referência liberada → destruição. Não combine Free manual com vida controlada por TInterfacedObject. A variante preserva o consumidor; o modelo de contagem não descreve todo ownership Delphi.

Abrir projeto de exemplo ↗