Atenção: esse artigo tem uma palestra complementar. Clique e assista!

Do que trata o artigo

Abordaremos o básico sobre Interfaces, a sua importância na POO, maneira correta de usar e como construir classes que podem ser usadas com interfaces. Trataremos também os benefícios de se usar classes já prontas do Delphi para se trabalhar com interfaces e como fazer com que objetos deleguem a implementação de uma interface a um outro objeto contido ou associado a ele.

Para que serve

Sendo uma técnica de programação orientada a objetos, as funcionalidades abordadas no artigo terão finalidade mais didática do que prática. Uma exceção é o uso de TAggregatedObject e TContainedObject, que pode demonstrar como objetos que implementam uma interface podem delegar essa tarefa a objetos agregados que realmente a implementam, economizando muitas linhas de código na confecção de wrappers.


Em que situação o tema é útil

Sempre que criarmos interfaces similares, porém sem grau de parentesco entre si, mas mesmo assim relacionadas. Por exemplo, quando desejamos encapsular a implementação de uma interface A em uma classe B que já implementa a interface B, sem precisar implementar os métodos da Interface A explicitamente, muito menos precisar tornar esta classe B um wrapper de outra classe A qualquer que implemente a interface A, economizando tempo e evitando códigos que misturem conceitos e responsabilidades.

Resumo do DevMan

Interfaces são essenciais nos nossos dias. Quem programa orientado a interfaces consegue criar sistemas altamente escalonáveis, com baixíssimo grau de acoplamento e de fácil manutenção.As interfaces ainda favorecem a separação de conceitos e responsabilidades e agilizam a programação em equipe, tornando as partes do sistema como “peças de Lego” que podem ser encaixadas de diversas formas.

O uso de interfaces também favorece um ambiente pseudo – gerenciado, onde o desenvolvedor precisa se preocupar em instanciar objetos, mas os contadores automáticos de referências do Delphi garantem que os mesmos serão destruídos da memória quando não forem mais necessários.

O paradigma da orientação a objetos nos ajuda a criar programas mais robustos, flexíveis e escalonáveis, isto é um fato. Mas um recurso que todas as linguagens orientadas a objeto possuem e poucas pessoas utilizam são as interfaces.

Interfaces ajudam a separar o desenvolvimento das classes ou módulos entre vários programadores, inclusive de empresas diferentes. Basta que as interfaces sejam combinadas ou convencionadas corretamente, como um contrato, e que tenham métodos com escopo, objetivos, entradas e saídas bem definidas.

Isso permite padronizar as interações e trocas de mensagens entre nossos objetos, e permite também criar objetos de linhagens diferentes que obedeçam as mesmas interfaces, para que possam ser intercambiáveis.

Num contexto dinâmico onde as regras de negócio podem ser modificadas ou o produto ser totalmente remodelado antes mesmo de ser posto em produção, o uso de interfaces nos garante uma grande flexibilidade. Separando nossos conceitos em partes encaixáveis nós podemos mapear facilmente quais implementações modificar, para atender uma regra de negócio sem comprometer um contrato, e quais interfaces devem ser estendidas através de herança.

Interfaces são um dos mais avançados recursos da Programação Orientada a Objetos e permitem mais um nível de abstração na sua análise. O Delphi suporta interfaces desde a versão 3, que na época foram desenvolvidas principalmente para integração com o COM.

Além dos objetivos mencionados, as interfaces também servem para simular herança múltipla. Você já deve ter ouvido falar que a linguagem C++, por exemplo, implementa a herança múltipla real, permitindo que uma classe possa ter duas ou mais classes base. Já o Delphi e o Java não possuem o recurso da herança múltipla para classes, mas possuem o recurso das interfaces, então embora uma classe só possa ser filha de outra classe, uma interface pode ser filha de duas ou mais interfaces ao mesmo tempo. Sendo assim uma classe que implementa uma interface filha de outras três interfaces pode ser considerada como descendente de múltiplas classes. A única forma de simular herança múltipla no Delphi seria através de interfaces.

Uma interface pode ser implícita, por exemplo, todo o conjunto de métodos e propriedades públicas de uma classe são a única interface que ela implementa implicitamente, mesmo que ela não implemente nenhuma interface explicitamente.

Todos os descendentes de uma mesma classe abstrata implementam a mesma interface implicitamente. Tanto é que na época do Delphi 2 e anteriores era possível usar COM sem o recurso das interfaces, usando classes abstratas no lugar. Lógico que a herança múltipla caía por terra.

Dito isto, você também pode entender uma interface como uma classe abstrata, que só tem a definição e não tem implementação, porém tem todos os recursos da POO, como herança e polimorfismo, além do recurso de contagem de referências e do identificador único guid.

Interfaces podem ser usadas para obrigar ou forçar que uma determinada classe tenha, ou implemente, determinados métodos. É isso que torna objetos de linhagens diferentes compatíveis entre si.

Nota: Para mais informações sobre como trabalhar com interfaces, sugiro a leitura das edições 74 e 75 da revista Clube Delphi.

Objetos que implementam interfaces, caso sejam descendentes de TInterfacedObject não necessitam ser destruídos, pois tem um contador de referências interno que automaticamente destrói o objeto que a implementa quando esse contador chega a zero. Além disso, esta classe TInterfacedObject tem um mecanismo para trabalhar com multithread que previne que um objeto seja destruído quando ainda não terminou de ser criado.

Então, teoricamente usando interfaces e criando nossas classes como descendentes de TInterfacedObject não precisamos nos preocupar com a destruição dos objetos e nem com Memory Leaks.

Nota: Um memory leak, ou vazamento de memória, é o termo dado para um objeto que foi criado em tempo de execução, contudo, após seu devido uso e descarte, não é destruído – eliminado da memória.

Trabalhando com interfaces

Vamos aprender alguns tópicos avançados sobre interfaces, como delegar a implementação de uma interface, como usar Aggregates e evitar memory leaks. Para fazer os nossos testes crie uma VCL Forms Application normal, com um único formulário chamado frmPrincipal e sua unit com o nome uFrmPrincipal.pas.

Todas as interfaces e classes que criaremos nós o faremos nessa unit, por simplicidade, e todos os testes que realizarmos serão executados em botões no formulário principal. Para podermos usar a função ShowMessage, assegure-se que a unit dialogs esteja citada na seção uses da sua unit uFrmPrincipal. Abra o arquivo de projeto, através do menu Project>View source e a seguinte linha de código que ativa um recurso muito interessante, existente desde a versão 2006, que é a detecção desses memory leaks:

ReportMemoryLeaksOnShutdown
:= true;

Essa variável de sistema faz com que o Delphi, ao encerrar a execução de um aplicativo, verifique a existência de algum vazamento de memória e reporta ao usuário o ocorrido. Dessa forma, o bloco de código (begin...end) deve se parecer como a seguir:


  begin
    ReportMemoryLeaksOnShutdown := true;
    Application.Initialize;
    Application.CreateForm(TfrmPrincipal, frmPrincipal);
    Application.Run;
  end. 

Agora na unit uFrmPrincipal adicione a interface e classes exibidas na Listagem1.

...

Quer ler esse conteúdo completo? Tenha acesso completo