Desmistificando as Interfaces no Delphi - Um exemplo simples (?)
Escrito por Carlos B. Feitoza Filho | |
Categoria: Artigos | |
Categoria Pai: Addicted 2 Delphi! | |
Acessos: 34140 |
Páginas dentro deste artigo
Um exemplo simples (?)
Gostaria de propor um exemplo hipotético simples, o qual vai fazer um uso básico de interfaces. Eu pretendo, com este exemplo, atingir o maior número de pessoas, fazendo-as entender o conceito de interface, por isso entenda que o que vou escrever a seguir não tem qualquer utilidade, fora a didática. Quero aproveitar para dizer também que este exemplo não tem o intuito de convencer ninguém a usar ou deixar de usar interfaces, ele apenas servirá para mostrar de forma nua e crua a diferença entre trabalhar com interfaces e trabalhar com classes puras.
Suponha que você precise criar uma estrutura de classes que representem pessoas, levando em conta que existem dois tipos de pessoa (pessoa física e pessoa jurídica) que considere os seguintes atributos:
- nome
- razão social
- idade
- sexo
- data de nascimento
- cpf
- cnpj
Existem duas abordagens clássicas que usam apenas classes. Uma delas utiliza todos os atributos em apenas uma classe com um campo para identificar o tipo de pessoa e a outra cria classes distintas. A primeira abordagem é muito ruim, porque mistura atributos de tipos distintos de pessoas, sendo assim, vou considerar a segunda abordagem clássica, a qual se pode ver a seguir:
unit UClasses;
interface
type
TSexo = (sMasculino, sFeminino);
TCPF = String[11];
TCNPJ = String[14];
TPessoa = class
private
function GetNome: String;
procedure SetNome(const Value: String);
function GetDataNascimento: TDateTime;
procedure SetDataNascimento(const Value: TDateTime);
public
property Nome: String read GetNome write SetNome;
property DataNascimento: TDateTime read GetDataNascimento write SetDataNascimento;
end;
TPessoaFisica = class(TPessoa)
private
function getCPF: TCPF;
function GetSexo: TSexo;
procedure setCPF(const Value: TCPF);
procedure SetSexo(const Value: TSexo);
public
property Sexo: TSexo read GetSexo write SetSexo;
property CPF: TCPF read getCPF write setCPF;
end;
TPessoaJuridica = class(TPessoa)
private
function GetCNPJ: TCNPJ;
function GetNomeFantasia: String;
procedure SetCNPJ(const Value: TCNPJ);
procedure SetNomeFantasia(const Value: String);
public
property CNPJ: TCNPJ read GetCNPJ write SetCNPJ;
property NomeFantasia: String read GetNomeFantasia write SetNomeFantasia;
end;
implementation
{As implementações dos métodos foram omitidas }
var
PF: TPessoaFisica;
PJ: TPessoaJuridica;
initialization
PF := TPessoaFisica.Create;
PJ := TPessoaJuridica.Create;
PF.Nome := 'José';
PJ.Nome := 'José Maria ME';
PF.CPF := '012344321712';
PJ.CNPJ := '91221133110010';
PF.Free;
PJ.Free;
end.
Como se pode ver, a abordagem clássica é bem limpa e simples. A classe TPessoa contém atributos que eu julgo comuns tanto para pessoas físicas como pessoas jurídicas. Nome seria o nome de batismo da pessoa física, ou a razão social de uma pessoa jurídica. DataNascimento seria a data de nascimento de uma pessoa física, ou a data de fundação de uma pessoa jurídica. Simplório e bobo, eu sei, mas funciona que é uma beleza. A interpretação da estruturação de objetos usando apenas classes é a seguinte:
TPessoa é uma classe geral que possui atributos de uma pessoa qualquer, enquanto TPessoaFisica e TPessoaJuridica são classes que definem pessoas de tipos distintos, cada uma delas com atributos específicos, porém ambas herdando de TPessoa, pois ambas são Pessoas
Vejamos agora como ficaria esta estrutura caso você quisesse usar interfaces de qualquer jeito, por ser um purista de OO ou um seguidor de modinhas:
unit UInterfaces;
interface
type
TSexo = (sMasculino, sFeminino);
TCPF = String[11];
TCNPJ = String[14];
IPessoa = interface
function GetNome: String;
procedure SetNome(const Value: String);
function GetDataNascimento: TDateTime;
procedure SetDataNascimento(const Value: TDateTime);
property Nome: String read GetNome write SetNome;
property DataNascimento: TDateTime read GetDataNascimento write SetDataNascimento;
end;
IPessoaFisica = interface(IPessoa)
function getCPF: TCPF;
function GetSexo: TSexo;
procedure setCPF(const Value: TCPF);
procedure SetSexo(const Value: TSexo);
property Sexo: TSexo read GetSexo write SetSexo;
property CPF: TCPF read getCPF write setCPF;
end;
IPessoaJuridica = interface(IPessoa)
function GetCNPJ: TCNPJ;
function GetNomeFantasia: String;
procedure SetCNPJ(const Value: TCNPJ);
procedure SetNomeFantasia(const Value: String);
property CNPJ: TCNPJ read GetCNPJ write SetCNPJ;
property NomeFantasia: String read GetNomeFantasia write SetNomeFantasia;
end;
TPessoa = class(TInterfacedObject,IPessoa)
private
function GetNome: String;
procedure SetNome(const Value: String);
function GetDataNascimento: TDateTime;
procedure SetDataNascimento(const Value: TDateTime);
end;
TPessoaFisica = class(TPessoa,IPessoaFisica)
private
function getCPF: TCPF;
function GetSexo: TSexo;
procedure setCPF(const Value: TCPF);
procedure SetSexo(const Value: TSexo);
end;
TPessoaJuridica = class(TPessoa,IPessoaJuridica)
private
function GetCNPJ: TCNPJ;
function GetNomeFantasia: String;
procedure SetCNPJ(const Value: TCNPJ);
procedure SetNomeFantasia(const Value: String);
end;
implementation
{ As implementações dos métodos foram omitidas }
var
PF: IPessoaFisica;
PJ: IPessoaJuridica;
initialization
PF := TPessoaFisica.Create;
PJ := TPessoaJuridica.Create;
PF.Nome := 'José';
PJ.Nome := 'José Maria ME';
PF.CPF := '012344321712';
PJ.CNPJ := '91221133110010';
{ Não é necessário liberar da memória porque interfaces possuem contagem de
referência e são liberadas automaticamente pelo Delphi quando as variáveis
saem do escopo }
end.
O código se tornou bem maior e por isso merece algumas explicações. Para começar, foram criadas 3 interfaces e 3 classes. Cada uma das 3 interfaces define métodos set/get e propriedades correspondentes que usam estes métodos, os quais não são implementados nas interfaces. Os métodos e propriedades em cada interface são coerentes com aquela interface, isto é, eles fazem sentido apenas para aquele interface, por exemplo, a interface IPessoaFisica contém a propriedade CPF, a qual só faz sentido para uma pessoa física, já a interface IPessoaJuridica contém a propriedade NomeFantasia, a qual, do mesmo modo, só faz sentido para uma pessoa jurídica. Cada uma dessas interfaces herda da interface IPessoa, a qual possui duas propriedades (e seus métodos set/get correspondentes) que eu julguei serem gerais, por poderem ser aplicados a qualquer pessoa, seja ela física ou jurídica. A herança de interfaces é semelhante a herança de classes, só que mais simples: interfaces que herdam de uma outra interface possuem todos os métodos e propriedades somados. IPessoaFisica possui seus métodos e propriedades, mais os métodos e propriedades de IPessoa, por exemplo.
A classe TPessoa herda de TInterfacedObject[1] ao mesmo tempo em que implementa a interface IPessoa, isso significa que nós somos obrigados a implementar em TPessoa todos os métodos existentes em IPessoa, de forma a satisfazer a interface. Isso exemplifica a primeira justificativa de uso de uma interface (Obrigar a implementação de métodos). TPessoaFisica e TPessoaJuridica ambos herdam de TPessoa e cada um deles implementa sua interface específica (IPessoaFisica e IPessoaJuridica), exemplificando a segunda justificativa de uso de uma interface (Introduzir comportamentos adicionais a uma classe). Aliás, vale salientar que não precisamos redeclarar as propriedades que foram definidas nas interfaces, precisamos apenas implementar os seus métodos set/get. A interpretação da estruturação de objetos usando interfaces é a seguinte:
TPessoa é uma classe que precisa implementar alguns métodos de forma obrigatória, ao passo que TPessoaFisica e TPessoaJuridica são pessoas, pois herdam de TPessoa, porém, cada uma delas tem características especiais que as diferem uma da outra, por isso, TPessoaFisica implementa IPessoaFisica e TPessoaJuridica implementa IPessoaJuridica. Ao final a classe TPessoaFisica define uma pessoa com características físicas, enquanto TPessoaJuridica define uma pessoa com caracterísiticas jurídicas