Artigo Clube Delphi 75 - POO no Delphi
Artigo da Revista Clube Delphi Edição 75.
Clique aqui para ler esse artigo em PDF.
POO
Programação Orientada a Objetos no Delphi
Conceitos e Implementação – Parte II
No artigo da edição anterior iniciamos a apresentação de alguns dos principais conceitos da programação orientada a objetos (POO) no Delphi, abordando classes, atributos, métodos, encapsulamento e herança. Estes conceitos foram apresentados através de exemplos a partir de um modelo de classes representando departamentos e seus funcionários, de diferentes tipos. Este artigo complementa o anterior abordando outros importantes conceitos de POO como polimorfismo, associação e interfaces, além de apresentar um estudo de caso que se utiliza dos conceitos apresentados em ambos os artigos, mostrando como podem ser utilizados na prática.
Polimorfismo
O polimorfismo é uma característica da Orientação a Objetos que permite aos objetos das classes terem comportamentos diferentes, sejam de seus ancestrais, da própria classe ou mesmo de outras classes. Assim, os métodos definidos e implementados em classes ancestrais, podem ser redefinidos e reimplementados em classes descendentes.
Para que isso funcione adequadamente, é necessário garantir qual implementação de um método será executada, visto que pode existir mais de uma com o mesmo nome, seja na hierarquia ou dentro da própria classe. Dessa forma o Delphi oferece um conjunto de palavras reservadas que permitem definir o comportamento das chamadas de métodos, ou seja, a ligação da chamada com a implementação desejada.
De acordo com sua forma de ligação, os métodos podem ser classificados em três tipos: static, virtual e dynamic. Os métodos estáticos (static) são o padrão do Delphi, ou seja, caso não sejam utilizadas explicitamente as diretivas virtual ou dynamic, os mesmos são considerados estáticos. A chamada desses tipos de métodos ativa a implementação feita na classe que instanciou o objeto.
Na hierarquia de funcionários, onde estão definidos os métodos polimórficos CalcularSalario e CalcularPremio, ainda não foram aplicadas diretivas nos métodos modificando o seu tipo e, por isso, os mesmos são estáticos.
Durante a execução do sistema, variáveis podem receber objetos criados a partir de classes diferentes do tipo declarado, uma vez que estejam numa mesma hierarquia. Dessa forma a ativação dos métodos ocorre de maneira diferente entre os objetos. Isso ocorre porque para métodos estáticos, a implementação ativada é a correspondente da classe que define a variável e não da classe que define sua instância.
Tal comportamento pode ser modificado para métodos virtuais e dinâmicos, através do uso das diretivas virtual e dynamic, em conjunto com a diretiva override. Assim, a chamada dos métodos passa a ativar a implementação do tipo em tempo de execução, ou seja, de acordo com o tipo da instância e não o tipo definido para a variável.
O uso de override, garante que apenas uma implementação do método exista em tempo de execução nas classes descendentes, ocultando as implementações dos ancestrais. Porém, esses métodos devem ter exatamente a mesma assinatura, ou seja, mesmo nome do método e mesmo número, ordem e tipos dos parâmetros.
As diretivas virtual e dynamic são equivalentes, sendo diferentes no sentido de que a virtual otimiza a velocidade de acesso e dynamic otimiza o tamanho do código, sendo a virtual a forma mais comum e eficiente de implementar o polimorfismo. Na Listagem 1 demonstra-se o efeito do uso dessas diretivas, através de algumas modificações na hierarquia de Funcionario.
Listagem 1. Definição da hierarquia de Funcionario com método virtual
type
Funcionario = class
function CalcularSalario: real; virtual;
end;
FuncionarioMensalista = class (Funcionario)
function CalcularSalario: real; override;
end;
FuncionarioDiarista = class (Funcionario)
function CalcularSalario: real; override;
end;
Na Listagem 1, utiliza-se na assinatura do método CalcularSalario na classe Funcionario, a diretiva virtual, enquanto que nas classes descendentes utiliza-se a diretiva override. As implementações dos métodos continuam as mesmas.
A Listagem 2 apresenta a utilização dos métodos com a diretiva override. Pressupõe-se que os valores dos campos dos objetos tenham sido modificados entre a criação dos objetos e a execução dos serviços CalcularSalario. A Listagem 2 deve ser implementada em outra unit que não a untClasses, devendo referenciá-la.
Listagem 2. Chamada de métodos virtuais com override
01: var
02: Func1: Funcionario;
03: Func2: FuncionarioDiarista;
04: begin
05: {Funcionario 1}
06: Func1 := FuncionarioMensalista.Create;
07: Func1.CalcularSalario;
08: {Funcionario 2}
09: Func2 := FuncionarioDiarista.Create;
10: Func2.CalcularSalario;
11: end;
Durante a execução do trecho de código apresentado na Listagem 2, as chamadas de métodos nas linhas 07 e 10 ativam a implementação correspondente das classes FuncionarioMensalista e FuncionarioDiarista respectivamente. Embora o tipo de dados da variável Func1 seja Funcionario (linha 02), o objeto armazenado nela é do tipo FuncionarioMensalista (linha 06). Sendo assim, em função da diretiva override, são executadas as implementações relativas às instâncias, e não relativas ao tipo da variável, como no caso dos métodos estáticos.
Vale a pena ressaltar que, para uso da diretiva override, a assinatura dos métodos deve ser exatamente igual, pois de outra forma é apresentado um erro durante a compilação. Além disso, override somente pode ser utilizado em conjunto com virtual ou dynamic, não podendo ser utilizado em métodos estáticos.
Para casos de polimorfismo onde a assinatura do método é diferente nos seus descendentes, podem ser utilizadas duas outras diretivas: overload e reintroduce.
A diretiva overload pode ser utilizada para qualquer tipo de método, seja estático, virtual ou dinâmico. Para métodos virtuais pode-se utilizar a diretiva reintroduce em conjunto para os descendentes. Além de permitir assinaturas diferentes de um mesmo método, a diretiva overload não oculta as implementações do método nos ancestrais, estando assim disponíveis mais de uma implementação simultaneamente.
Para ativar a implementação correta, são analisados os parâmetros utilizados na chamada do método, executando assim a implementação que corresponder a esses parâmetros. Porém, para os métodos virtuais, caso seja necessário, os métodos dos ancestrais podem ser ocultados, utilizando a diretiva " [...] continue lendo...
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo