1 de setembro de 2009

Usando interfaces em Delphi - parte 2

Há alguma confusão quando se fala em interface em programação porque o termo remete à tecnologia COM da Microsoft e se mistura com ele. Colabora para a confusão o fato de que a interface base em Delphi tem o nome de IUnknown, o mesmo de uma interface amplamente utilizado no COM e que tem o mesmo objetivo, que é controlar a quantidade de referências a um objeto e também permitir que se descubra se esse objeto implementa ou não uma interface arbitrária. Na verdade, o projeto do COM é centrado no conceito de interfaces e, portanto, não poderia ter sido implementado no formato atual sem elas. E, quando o recurso de interfaces no Delphi foi desenhado, tinham como alvo a estrutura necessária para interagir com COM de modo que trabalhar com essa tecnologia fosse algo trivial.

Apesar de ter uma ligação tão intrincada com o COM, mostrei no post anterior que o conceito de interfaces em Delphi pode ser usado de forma desvinculada. Mesmo assim, a escolha de introduzir interfaces tendo o COM em mente produziu alguns efeitos colaterais que não podem ser negligenciados durante o projeto e programação de sistemas onde se queira usar esse recurso.

Em primeiro lugar, as interfaces em Delphi tem suas referências controladas através da implementação do IUnknown. Sempre que você atribui o ponteiro de um objeto ou de uma interface a uma variável de interface, o contador de referências é incrementado. Quando a variável de interface sai de escopo, o contador é automaticamente decrementado e quando chega a zero, o objeto original é removido da memória. Isso foge da forma tradicional de se controlar o ciclo de vida de um objeto em programas Win32 já que a implementação do IUnknown funciona como um garbage collector. Isto é, você cria o objeto que implementa uma interface mas é o compilador quem decide o momento em que o destructor será invocado.

No entanto, se o ponteiro do objeto que você criou jamais for atribuído a uma variável do tipo interface, você ainda é responsável por destruir esse objeto! É fácil ver a bagunça que vai virar se o planejamento não for bem feito, com memória sendo perdida ou erros de access violation sendo gerados do nada. Quando usado em conjunto com o COM, entretanto, a estrutura de criação através de CoClasses se encarregará de destruir corretamente o objeto.

Formas para resolver esse impasse podem ser encontradas num artigo publicado no EDN (Embarcadero Developer Network) : Delphi reference counted interfaces.

O segundo ponto com o qual se preocupar é com a necessidade de recuperar um ponteiro para a classe original a partir de um ponteiro da interface. Isto é, você instanciou a classe, atribuiu o ponteiro a uma interface e agora quer recuperar de novo o ponteiro para a classe. No Delphi, variáveis do tipo class são diferentes de variáveis do tipo interface e elas não são intercambiáveis como em outras linguagens de programação menos ligadas ao COM. De fato, uma variável para classe contem o endereço de memória onde estão os dados dessa classe enquanto que a interface é apenas um ponteiro para a tabela virtual de métodos dessa interface. São, portanto, incompatíveis e isso deve fazer parte do planejamento da hierarquia de classes e interfaces.

Por fim, as interfaces do Delphi funcionam melhor quando são vinculadas a um GUID pois a base para verificar se uma classe implementa uma determinada interface é a função QueryInterface introduzida pelo IUnknown e usada à exaustão quando se trabalha com interfaces. E essa função pede uma identificação única na forma de um GUID para a interface que se deseja pesquisar ou instanciar. Você pode pressionar as teclas <Ctrl-Shift-G> no IDE do Delphi para ter um novo GUID gerado. Ao declarar uma interface, inclua o GUID como no exemplo:
IWSortable = interface
['{7CEF3AE4-9127-460E-B1A1-8D18B7CA7D05}']
function CompareTo (Elemen: IWSortable): Integer;
end;

Volto em outra oportunidade com esse assunto para mostrar um implementação funcional de interface de sort.

Nenhum comentário :

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.