Delphi – Interfaces

Definição

Imagine que você deseja contratar um plano de internet para a sua casa. Após algumas pesquisas, você descobre que existem 4 operadoras disponíveis na sua região.

De certo modo, as quatro operadoras funcionam do mesmo modo, você adere ao serviço, eles vão até a sua casa para instalar os equipamentos, você consegue acessar a internet, e por ultimo, você começar a pagar mensalmente.

Cada operadora de internet tem suas peculiaridades, uma é mais cara, outra a internet é mais lenta, outra instala mais rapidamente e a outra demora mais, porem tem um atendimento melhor, porem todas elas tem características similares:

  • Contratar Serviço
  • Instalar Equipamento
  • Acessar Internet
  • Cobrança Mensal

Se agruparmos todas essas características que as operadoras compartilham, podemos criar uma “Interface” com esses quatro métodos, exemplo:

IProvedorDeInternet = interface
  ['{C4D616D7-7562-464E-B9E5-DF899A8052F1}']
  function ContratarServico: boolean;
  function SolicitarInstalacao: boolean;
  procedure AcessarInternet;
  procedure PagarMensalidade;
end;

Note que a interface apenas contem a descrição do que terá que ser implementado e uma identificação (o código GUID), não existe implementação de fato nela. Cabe a uma classe concreta implementar os métodos propriamente ditos.

Voltando ao nosso exemplo, caso cada operadora de internet tivesse sua própria classe, ela tem que explicitamente informar que ela implementa determinada interface, exemplo:

TOperadoraVivo = class(TInterfacedObject, IProvedorDeInternet)
private
  // ...
public
  function ContratarServico: boolean;
  function SolicitarInstalacao: boolean;
  procedure AcessarInternet;
  procedure PagarMensalidade;
end;

TOperadoraClaro = class(TInterfacedObject, IProvedorDeInternet)
private
  // ...
public
  function ContratarServico: boolean;
  function SolicitarInstalacao: boolean;
  procedure AcessarInternet;
  procedure PagarMensalidade;
end;

TOperadoraMorto = class(TInterfacedObject, IProvedorDeInternet)
private
  // ...
public
  function ContratarServico: boolean;
  function SolicitarInstalacao: boolean;
  procedure AcessarInternet;
  procedure PagarMensalidade;
end;

TOperadoraEscuro = class(TInterfacedObject, IProvedorDeInternet)
private
  // ...
public
  function ContratarServico: boolean;
  function SolicitarInstalacao: boolean;
  procedure AcessarInternet;
  procedure PagarMensalidade;
end;

Resumo: quando a sua classe adere a um contrato (interface) ela tem que entregar (implementar) tudo aquilo que o contrato diz que ela faz, pois quem irá utilizar a sua classe não quer saber como você faz, só quer saber se você faz.

Vantagens

A grande vantagem de se usar interfaces de programação é o fato que você não precisa conhecer a forma que algo é implementado, você precisa saber o que uma classe lhe fornece. Voltando ao nosso exemplo, vamos supor que você está alugando uma casa, e um requisito seu é que tenha internet para acessar, você não precisar saber se é da Claro, da Vivo, da Morto ou da Escuro, você precisa que ela funcione. No nosso código você precisa que a classe que você irá usar implemente “IProvedorDeInternet”, caso implemente, você pode usar, caso contrario não. Exemplo:

interface

TMinhaCasa = class
public
  FMeuProvedor: IProvedorDeInternet;
end;

implementation
...
FMeuProvedor := TOperadoraVivo.Create;
FMeuProvedor.AcessarInternet;

Caso após algum tempo você mude de casa ou queira mudar de operadora, é só trocar por outra classe que tenha o mesmo comportamento, ou seja, implemente a mesma interface.

Outra vantagem do uso de interfaces é o encapsulamento de código, pois você acaba centralizando em uma determinada classe a lógica daquele assunto, deixando o resto do seu projeto apenas utilizando o contrato que disponibiliza aquela serviço, sem acesso direto a implementação do serviço.

FMeuProvedor := TOperadoraMorto.Create;
FMeuProvedor.AcessarInternet;

Outra vantagem para o uso de interfaces é o gerenciamento de memória. No caso de classes que implementam interfaces, o próprio compilador se encarregar de destruir a nossa instância quando ela não estiver mais em uso. Para que isto ocorra é necessário alguns cuidados:

  • A interface tem que ter uma assinatura, que é um código “GUID”, no nosso exemplo é a linha contendo a identificação “[‘{C4D616D7-7562-464E-B9E5-DF899A8052F1}’]”. Através deste código o compilador conta quantas instâncias existem de uma interface e sabe onde elas estão sendo usadas.
  • A implementação da classe tem que derivar da classe “TInterfacedObject”. Está classe implementa os procedimentos de contagem de uso da sua classe. Caso não queira derivar sua classe desta classe tudo bem, mas você terá que implementar os métodos contidos em “TInterfacedObject” de forma manual na sua classe, não é algo difícil, mas pra que ter mais trabalho não?!
  • A variável onde será alocado o ponteiro da sua instância tem que ser do tipo da interface, e não da classe que implementa a interface. Exemplo:
var
  vMeuProvedor: IProvedorDeInternet;

Gerenciamento de Memória

Você pode gerenciar automaticamente a liberação de memória dos seus objetos ao usar interface. Ao adicionar “TInterfacedObject” a sua implementação, você está inserindo nela a implementação de um contador de referencias, e este contador de referencias.

Este contador de referencias sempre é incrementado quando um procedimento começa a usar o seu objeto e sempre é reduzido quando um procedimento termina de usar seu objeto. Com esta conta feita, o compilador sabe o momento em que seu objeto não está mais sendo usado, conseguindo destrui-lo automaticamente logo após o uso.

Para este gerenciamento funcionar, os seguintes requisitos são necessários:

  • A implementação tem que derivar de “TInterfacedObject”.
  • Necessário atribuído um GUID a classe, através dele que o compilador sabe identificar as diferentes classes.
  • A variável onde o ponteiro está guardado tem que ser do tipo da Interfaces e não do Tipo da Classe.

Observações: ao passar um objeto que seja do tipo de uma interface, se o parâmetro for do tipo “const”, a referencia de uso não será incrementada do objeto, caso contrario, o tipo do parâmetro não esteja definido ou seja do tipo “var”, o incremento será feito e você terá que reduzir a contagem de referencia manualmente.

Desvantagens

O uso de interfaces exige uma pouco mais de analise no momento de estrutura seu código. Você precisa identificar essas características que podem se encaixar em um “contrato”.

Outro ponto desfavorável é que exige um pouco mais de programação, precisamos gastar um pouco mais dos nossos dedos e criar uma camada a mais em nosso código.

Deixe um comentário

Blog no WordPress.com.

Acima ↑