
Última atualização em 9 de outubro de 2024 por Willian Tuttoilmondo
O que são interfaces
Dentro do conceito de desenvolvimento orientado a objetos, uma interface é a delimitação de um componente da aplicação, determinando quais são seus membros – métodos e propriedades – e como estes serão implementados pelos componentes que a utilizem. Por ter nascido como uma linguagem fortemente orientada a objetos, a implementação de interfaces em Delphi é algo absolutamente natural. É comum vermos, numa análise mais profunda de elementos já existentes no IDE Delphi, que várias interfaces já foram implementadas e são amplamente utilizadas. Como vimos no artigo sobre a instituição de um componente de registro de log thread safe, a classe TMultiReadExclusiveWriteSynchronizer implementa a interface IReadWriteSync constante na unit System.SysUtils.pas.
Como já contamos com esses recursos na linguagem, implementar interfaces em Delphi é simples mas, antes de começarmos, precisamos entender quando devemos usá-las, assim como implementá-las e, principalmente, o porquê do seu uso. Além disso, alguns truques legais com o uso de interfaces serão mostrados como parte da utilização do paradigma de programação funcional em Delphi.
Interfaces em Delphi – quando
Um dos grandes desafios do desenvolvimento em Delphi é entender quando usar determinados recursos, e com interfaces não seria diferente. Um dos grandes pontos a serem observados quando vamos usar interfaces em Delphi é se a minha arquitetura, além de admitir a orientação a objetos, também vai implementar a segmentação de um ou mais componentes em seu desenvolvimento. Na prática, o que preciso entender é se o que vou desenvolver pode ser segregado em várias pequenas parcelas para facilitar tanto o desenvolvimento quanto a manutenção.
Quando vimos os padrões de projeto para implementação da nossa calculadora e, posteriormente, nossa API RESTfull, ficamos apenas com a orientação a objetos sem a implementação de interfaces pois, naquele momento, era mais simples compreender como os padrões funcionavam, tanto separadamente em conjunto. Agora que já sabemos como usá-los, podemos melhorá-los usando interfaces. Para isso, vamos rever como construímos as classes envolvidas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | unit Classes.Operacao; interface type TOperacao = class abstract strict private FB: Extended; FA: Extended; procedure SetA(const Value: Extended); procedure SetB(const Value: Extended); public constructor Create; virtual; function Efetuar: Extended; virtual; abstract; property A: Extended read FA write SetA; property B: Extended read FB write SetB; end; TOperacaoClass = class of TOperacao; implementation { TOperacao } constructor TOperacao.Create; begin FA := 0; FB := 0; end; procedure TOperacao.SetA(const Value: Extended); begin if FA = Value then begin Exit; end; FA := Value; end; procedure TOperacao.SetB(const Value: Extended); begin if FB = Value then begin Exit; end; FB := Value; end; end. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | unit Classes.Soma; interface uses Classes.Operacao; type TSoma = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TSoma } function TSoma.Efetuar: Extended; begin Result := A + B; end; end. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | unit Classes.Subtracao; interface uses Classes.Operacao; type TSubtracao = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TSubtracao } function TSubtracao.Efetuar: Extended; begin Result := A - B; end; end. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | unit Classes.Multiplicacao; interface uses Classes.Operacao; type TMultiplicacao = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TMultiplicacao } function TMultiplicacao.Efetuar: Extended; begin Result := A * B; end; end. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | unit Classes.Divisao; interface uses Classes.Operacao; type TDivisao = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TDivisao } function TDivisao.Efetuar: Extended; begin Result := A / B; end; end. |
Como vimos, a orientação a objetos foi bem executada, implementando bem o paradigma. Mas como nosso intuito é estudar o uso de interfaces em Delphi, vamos entender as necessidades envolvidas para começarmos essa “melhoria”. A classe TOperacao é uma classe abstrata, ou seja, nunca será instanciada, e que contém todos os elementos comuns às classes descendentes. Sendo assim, ela foi desenvolvida com o propósito exclusivo de delimitar o escopo mínimo de como as classes descendentes devem ser.
Ao analisarmos o princípio envolvido na definição de interfaces, em especial no uso de interfaces em Delphi, veremos que eles são bem similares. Interfaces também delimitam o mesmo escopo mínimo de como classes serão desenvolvidas, com seus membros – métodos e propriedades – delimitados. A diferença reside no fato de que interfaces não possuem implementação, sendo puramente abstratas. Classes abstratas, ao contrário, devem conter implementações em tudo aquilo que não for definido como abstrato. Isso implica em dizer que interfaces não “geram código”, mesmo delimitando como classes serão desenvolvidas.
Aqui chegamos ao quando: nós usaremos interfaces em nosso desenvolvimento toda vez em que quisermos, ou precisarmos, simplificar o consumo de componentes de uma aplicação. Toda vez em que queríamos utilizar um descendente de TOperacao, precisávamos invocar sua instância, utilizá-la e destruí-la em seguida. Com interfaces, precisaríamos apenas invocar sua instância e utilizá-la, sem nos preocuparmos com a liberação do objeto da memória. Mesmo sabendo que o Delphi não possui recursos como o garbage collector, presente em linguagens como Java ou C#, o “Delphi” é quem será responsável por essa instância.
Se dermos uma olhada na classe TInterfacedObject, da qual toda classe que implementa uma interface em Delphi deve ser herdada, notaremos que ela possui alguns elementos, como os vistos abaixo:

Alguns membros desta classe, que deriva de TObject, fazem a gestão das instâncias que são criadas ao longo do desenvolvimento. Os métodos _AddRef e _Release têm nomes bem sugestivos e que falam por si. Enquanto o primeiro adiciona a referência ao objeto instanciado em memória para que este seja controlado através dele mesmo, o segundo libera esta instância da memória, eliminando tanto a necessidade de fazê-lo como o efeito colateral de não fazê-lo, o temido memory leaking – vazamento de memória, em inglês -, o qual pode trazer efeitos indesejados bem complicados, como o aumento absurdo do consumo de memória de uma aplicação por deixarmos de destruir uma instância de um objeto criado ciclicamente, por exemplo.
Como já desenvolvemos uma API RESTful no passado, e vamos expandi-la no futuro, seria interessante que, por exemplo, os objetos instanciados para responder às solicitações não permanecessem em memória, causando o aumento de consumo de memória no servidor e resultando, em um primeiro momento, numa queda sensível em seu desempenho e, posteriormente, na interrupção do serviço em si.
Outra oportunidade para usarmos interfaces em Delphi é quando precisamos que classes distintas compartilhem membros mesmo não sendo descendentes de uma mesma classe base. Quando, no futuro, discutirmos os princípios de SOLID mais a fundo, veremos o quão importante é o princípio de segregação de interfaces e que ele nos diz que, ao invés de termos interfaces genéricas e de amplo uso, com várias propriedades e métodos definidos, devemos quebrá-la em interfaces menores e mais objetivas, de modo que as implementações em futuras classes sejam igualmente objetivas, simplificando nosso desenvolvimento.
Ok, tudo isso, no papel, é tão lindo mas, na prática, como faço isso?
Interfaces em Delphi – como
Como já entendemos quando podemos usar interfaces em Delphi, vamos agora ver como efetivar essa “melhoria”. Para isso, vamos mudar um pouco as coisas, começando pela classe TOperacao.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | unit Classes.Operacao; interface type IOperacao = interface ['{66390E64-3FD8-486B-BDA1-86C50BA9A830}'] function Efetuar: Extended; function GetA: Extended; function GetB: Extended; procedure SetA(const Value: Extended); procedure SetB(const Value: Extended); property A: Extended read GetA write SetA; property B: Extended read GetB write SetB; end; TOperacao = class abstract(TInterfacedObject, IOperacao) strict private FB: Extended; FA: Extended; function GetA: Extended; function GetB: Extended; procedure SetA(const Value: Extended); procedure SetB(const Value: Extended); public constructor Create; virtual; function Efetuar: Extended; virtual; abstract; end; TOperacaoClass = class of TOperacao; implementation { TOperacao } constructor TOperacao.Create; begin FA := 0; FB := 0; end; function TOperacao.GetA: Extended; begin Result := FA; end; function TOperacao.GetB: Extended; begin Result := FB; end; procedure TOperacao.SetA(const Value: Extended); begin if FA = Value then begin Exit; end; FA := Value; end; procedure TOperacao.SetB(const Value: Extended); begin if FB = Value then begin Exit; end; FB := Value; end; end. |
É importante notar que, como tudo na vida, a implementação de interfaces em Delphi tem suas regras. Não são regras difíceis de respeitar, é verdade, mas são muito importantes para o desenvolvimento. Sendo assim, interfaces em Delphi:
- Não podem conter variáveis de campo;
- Não podem conter construtores, destrutores ou class methods;
- Não podem ter métodos marcados com as diretivas virtual ou dynamic;
- Devem ter suas propriedades lidas através de getters e, quando aplicável, escritas através de setters;
- Podem ter seus métodos sobrecarregados, desde que possuam assinaturas diferentes e sejam marcados com a diretiva overload e;
- Devem possuir seu identificador único, no formato GUID, no escopo de sua definição.
Como parte da convenção utilizada no desenvolvimento, ao criarmos uma interface em Delphi, devemos, assim como usamos a letra T para identificar uma classe, usar a letra I para identificá-la. Sendo assim, nossa interface receberá o nome de IOperacao e será do tipo interface. Logo abaixo da definição do nome, e tipo, da interface, temos o valor [‘{66390E64-3FD8-486B-BDA1-86C50BA9A830}’], sendo este o identificador único desta. Para obtê-lo, basta segurar as teclas Ctrl+Shift+G uma única vez. Este atalho do IDE gera, toda vez que utilizado, um novo GUID, não os repetindo nunca.
Definida a interface, vamos dispor em seu escopo – e notem que não há nenhuma seção delimitada para a visibilidade – tudo aquilo que deveremos implementar em classes que a utilizem. Sendo assim, colocamos o método Efetuar, as propriedades A e B, assim como seus getters e setters. Notem, também, que se utilizarmos o atalho Ctrl+Shift+C, nenhum código será gerado na seção de implementação da unit utilizada. Isso se deve ao fato de que, como disse antes, interfaces em Delphi não possuem implementação.
Já a classe TOperacao, a qual implementará a interface, será levemente modificada. Além de ser, agora, derivada de TInterfacedObject, também recebe em seu escopo a indicação de que implementará a interface IOperacao, o que nos obriga a implementar nela tudo o que foi definido na interface. Mas, aqui, há um detalhe bem interessante: quando definimos propriedades em uma interface em Delphi, não somos obrigados a replicá-la na classe que a implementa. Isso, como parte das boas práticas, obriga o desenvolvedor a, quando precisar utilizar a propriedade, definir a instância do objeto como derivada da interface e não da classe, o que nos poupa a dor de cabeça com os vazamentos de memória.
Ah, vale lembrar que o método Efetuar ainda não possui implementação na classe TOperacao e, com isso, deve permanecer abstrato nesta classe. E como já modificamos a classe TOperacao para implementar IOperacao, vejamos como ficam as classes descendentes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | unit Classes.Soma; interface uses Classes.Operacao; type TSoma = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TSoma } function TSoma.Efetuar: Extended; begin Result := A + B; end; end. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | unit Classes.Subtracao; interface uses Classes.Operacao; type TSubtracao = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TSubtracao } function TSubtracao.Efetuar: Extended; begin Result := A - B; end; end. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | unit Classes.Multiplicacao; interface uses Classes.Operacao; type TMultiplicacao = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TMultiplicacao } function TMultiplicacao.Efetuar: Extended; begin Result := A * B; end; end. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | unit Classes.Divisao; interface uses Classes.Operacao; type TDivisao = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TDivisao } function TDivisao.Efetuar: Extended; begin Result := A / B; end; end. |
Como é possível notar, nenhuma alteração se fez necessária, uma vez que classes herdadas de outras classes que implementam interfaces em Delphi não precisam implementar novas interfaces, exceto quando necessário. Como modificamos a classe TOperacao e utilizamos uma abstract factory para invocar instâncias das classes que descendem dela, também precisaremos alterá-la para abrigar essa alteração e, de quebra, também a transformaremos em uma interface e a implementaremos através de sua respectiva classe.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | unit Classes.Factory; interface uses System.Generics.Collections, Classes.Operacao; type IFactory = interface ['{7E029A53-40D8-4CAF-9C6A-5DAC2B91BE26}'] function GetOperacoes: TArray<string>; function RegisterClass(const AKey: string; const AClass: TOperacaoClass): IFactory; function GetOperacao(const AKey: string): IOperacao; property Operacoes: TArray<string> read GetOperacoes; end; TFactory = class(TInterfacedObject, IFactory) strict private FDictionary: TObjectDictionary<string, TOperacaoClass>; function GetOperacoes: TArray<string>; public constructor Create; destructor Destroy; class function Instance: IFactory; function RegisterClass(const AKey: string; const AClass: TOperacaoClass): IFactory; function GetOperacao(const AKey: string): IOperacao; end; implementation uses System.SysUtils; var FFactory: TFactory; { TFactory } constructor TFactory.Create; begin FDictionary := TObjectDictionary<string, TOperacaoClass>.Create; end; destructor TFactory.Destroy; begin FreeAndNil(FDictionary); end; function TFactory.GetOperacoes: TArray<string>; begin Result := FDictionary.Keys.ToArray; end; function TFactory.GetOperacao(const AKey: string): IOperacao; var Operacao: TOperacaoClass; begin Result := nil; if not (FDictionary.TryGetValue(AKey, Operacao) and Assigned(Operacao)) then begin Exit; end; Result := Operacao.Create; end; class function TFactory.Instance: IFactory; begin if not Assigned(FFactory) then begin FFactory := TFactory.Create; end; Result := FFactory; end; function TFactory.RegisterClass(const AKey: string; const AClass: TOperacaoClass): IFactory; begin Result := Self; if FDictionary.ContainsKey(AKey) then begin Exit; end; FDictionary.Add(AKey, AClass); end; initialization FFactory := nil; end. |
Já de início notamos que, em comparação à implementação anterior, a função GetOperação não retornará mais um objeto baseado em TOperacao, mas em IOperacao. Isso se deve ao fato de que, quando implementamos interfaces em Delphi, podemos definir variáveis que utilizam a interface ao invés da classe para seu tipo. O mesmo vale para funções e parâmetros de métodos. Outra coisa que notamos é o fim da seção finalization, a qual destruía a instância da abstract factory, algo que não é mais necessário.
Além disso, o método RegisterClass, que anteriormente era um procedimento, passou a ser uma função que retorna a própria instância de IFactory. Isso nos dará um ganho em termos de codificação, já que posso retornar as instâncias das próprias interfaces em Delphi para diminuir o número de linhas necessárias para obter um resultado. Sendo assim, para otimizar a utilização de recursos, vamos unificar as units que definem as classes descendentes de TOperacao e utilizar o recurso desenvolvido no método RegisterClass.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | unit Classes.Operacoes; interface uses Classes.Operacao; type TSoma = class(TOperacao) public function Efetuar: Extended; override; end; TSubtracao = class(TOperacao) public function Efetuar: Extended; override; end; TMultiplicacao = class(TOperacao) public function Efetuar: Extended; override; end; TDivisao = class(TOperacao) public function Efetuar: Extended; override; end; implementation uses Classes.Factory; { TSoma } function TSoma.Efetuar: Extended; begin Result := A + B; end; { TSubtracao } function TSubtracao.Efetuar: Extended; begin Result := A - B; end; { TMultiplicacao } function TMultiplicacao.Efetuar: Extended; begin Result := A * B; end; { TDivisao } function TDivisao.Efetuar: Extended; begin Result := A / B; end; initialization TFactory.Instance.RegisterClass('soma', TSoma) .RegisterClass('subtracao', TSubtracao) .RegisterClass('multiplicacao', TMultiplicacao) .RegisterClass('divisao', TDivisao); end. |
Assim como a implementação anterior fazia, aqui também registramos as classes TSoma, TSubtracao, TMultiplicacao e TDivisao na abstract factory, mas com uma grande diferença: enquanto antes faríamos uma instrução de registro por linha, aqui usamos a mesma linha de código para realizar o registro de todas as classes. Como disse antes, quando utilizamos interfaces em Delphi, isso pode trazer essas pequenas vantagens. Tudo bem, isso não é exclusividade, nem das interfaces, muito menos da implementação de interfaces em Delphi, já que isso pode ser feito em classes também, mas é algo que veio com elas e isso torna tudo mais legal.
Bem, já entendemos quando e como fazer, mas por que eu deveria me preocupar com isso afinal?
Interfaces em Delphi – porque
Esta talvez seja, das três perguntas que respondemos, a mais simples delas. Utilizar e implementar interfaces em Delphi é vantajoso porque:
- Segmenta a definição de nossas regras de negócio em interfaces mais simples, com menor demanda de implementação;
- Controla melhor o consumo de memória, fazendo com que instâncias de objetos baseadas em interfaces sejam automaticamente destruídas quando não utilizadas;
- Facilita a utilização de recursos para simplificar a codificação, demandando menos linhas de código para se obter um resultado e;
- É muito mais elegante implementar interfaces em Delphi, principalmente quando desenvolvemos APIs.
Seja lá qual for a sua razão para ter chegado até aqui, seja para aprendizado ou evolução, tenha em mente que implementar interfaces em Delphi, ao contrário do que diz o senso comum, é muito simples e, com certeza, muito mais eficiente do que qualquer outra arquitetura de orientação a objetos que seja adotada. E, além disso, seu chefe vai adorar saber que você conhece um assunto que tão poucos dominam no mercado, assim como o próprio mercado vai te ver com outros olhos a partir disso.
Interfaces em Delphi – truques
Para implementar interfaces em Delphi basta conhecer como fazer, o que já vimos aqui. Mas existem alguns truques que são bem interessantes de se usar e que justificam a adoção desse modelo de desenvolvimento. Como vimos na transformação da abstract factory em uma interface, ter métodos como funções que retornam a própria instância da interface em Delphi faz com que ganhemos produtividade em nosso código. Sendo assim, podemos melhorar a interface IOperacao para facilitar ainda mais a nossa vida.
Contudo, para isso, vamos lançar mão de um segundo truque: fazer a escrita de uma propriedade ser uma função que retorna a própria interface. Assim como funções expostas publicamente, podemos criar outras funções nas interfaces em Delphi para setar o valor de uma propriedade e, assim, ganharmos tempo com sua implementação e uso. Sendo assim, vejamos como ficaria a interface IOperacao.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | unit Classes.Operacao; interface type IOperacao = interface ['{66390E64-3FD8-486B-BDA1-86C50BA9A830}'] function Efetuar: Extended; function GetA: Extended; function GetB: Extended; procedure PutA(const Value: Extended); procedure PutB(const Value: Extended); function SetA(const AValue: Extended): IOperacao; function SetB(const AValue: Extended): IOperacao; property A: Extended read GetA write PutA; property B: Extended read GetB write PutB; end; TOperacao = class abstract(TInterfacedObject, IOperacao) strict private FB: Extended; FA: Extended; function GetA: Extended; function GetB: Extended; procedure PutA(const Value: Extended); procedure PutB(const Value: Extended); public constructor Create; virtual; function Efetuar: Extended; virtual; abstract; function SetA(const AValue: Extended): IOperacao; function SetB(const AValue: Extended): IOperacao; end; TOperacaoClass = class of TOperacao; implementation { TOperacao } constructor TOperacao.Create; begin FA := 0; FB := 0; end; function TOperacao.GetA: Extended; begin Result := FA; end; function TOperacao.GetB: Extended; begin Result := FB; end; procedure TOperacao.PutA(const Value: Extended); begin if FA = Value then begin Exit; end; FA := Value; end; procedure TOperacao.PutB(const Value: Extended); begin if FB = Value then begin Exit; end; FB := Value; end; function TOperacao.SetA(const AValue: Extended): IOperacao; begin PutA(AValue); Result := Self; end; function TOperacao.SetB(const AValue: Extended): IOperacao; begin PutB(AValue); Result := Self; end; end. |
Assim, quando quisermos usar os métodos SetA e SetB para definir o valor das propriedades A e B, poderemos fazer isso em uma única linha. Como já temos uma API desenvolvida usando essas classes, vamos ver como ficaria o método que retorna o resultado da operação efetuada na API. Antes, tínhamos isso:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | objOperacao := TFactory.Instance.GetOperacao(strClass); objOperacao.A := strA.ToFloat; objOperacao.B := strB.ToFloat; with TJSONObject.Create do begin try try AddFloatPair('result', objOperacao.Efetuar); Response.Content := Minify; Response.StatusCode := 200; except on E: Exception do begin case E is EZeroDivide of True : raise Exception.Create('O valor de b não pode ser 0 em uma divisão.'); False: raise; end; end; end; finally DisposeOf; end; end; objOperacao.DisposeOf; |
Agora, com a implementação através de interfaces em Delphi, podemos implementar nosso código desta forma:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | with TJSONObject.Create do begin try try AddFloatPair('result', TFactory.Instance.GetOperacao(strClass).SetA(strA.ToFloat) .SetB(strB.ToFloat) .Efetuar); Response.Content := Minify; Response.StatusCode := 200; except on E: Exception do begin case E is EZeroDivide of True : raise Exception.Create('O valor de b não pode ser 0 em uma divisão.'); False: raise; end; end; end; finally DisposeOf; end; end; |
Com isso, extinguimos a necessidade de:
- Controlar a instância do objeto que faz a operação;
- Criar uma variável para abrigar esta instância;
- Utilizar uma linha para sua atribuição, assim como para a definição dos valores em suas propriedades e;
- Destruir a instância do objeto manualmente.
Beleza, mas e agora?
Se você já chegou até aqui, uma das principais perguntas é: posso implementar isso nas minha aplicações legadas? A resposta é simples: SIM! Mais do que isso, recomendo fortemente que você passe adotar interfaces em Delphi para todo e qualquer projeto orientado a objetos, uma vez que isso vai trazer incontáveis ganhos, não só na qualidade do que é desenvolvido, mas também na velocidade com que você vai desenvolver.
Além disso, interfaces em Delphi são mais fáceis de manter, requerendo intervenções apenas nas parcelas de código que as implementam. Mas isso já é sobre SOLID, algo que discutiremos em breve.
Faça um comentário