Zetta-Ømnis Soluções Tecnológicas
Desenvolvendo hoje a tecnologia do amanhã
Visite Nosso Patrocinador
Você está aqui:
DO NOT UNDERSTAND PORTUGUESE? CLICK ON THE FLAG TO CHANGE THE LANGUAGE!
ptarzh-CNzh-TWnlenfrdeeliwhihugaitjakonororues

Desmistificando as Interfaces no Delphi

Imagem meramente ilustrativa

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


1 Cada interface básica herda de IInterface a qual possui alguns métodos que precisam ser implementadas obrigatoriamente. Quando uma classe implementa uma interface qualquer, ela precisa implementar todos os métodos desta interface, incluindo todos os métodos das interfaces das quais esta interface herda e isso inclui os métodos existentes em IInterface. TInterfacedObject é uma classe que já implementa os métodos de IInterface, de forma que você não precise se preocupar em implementá-los, já que eles não fazem parte da sua programação. IInterface está para as interfaces assim como TObject está para as classes
Acesso Rápido
Não digite mais que o necessário...



Escaneie este QRCode em dispositivos móveis para acessar a página atual rapidamente nestes dispositivos
Conteúdo Verificado!
#BULLSHITFREE #CLICKBAITFREE
#MONEYLESS
Este site é amigo do desenvolvedor do mundo real
Gostou do conteúdo?
Se você gostou do conteúdo que está lendo, você pode ajudar a manter este site no ar doando qualquer quantia. Use os botões abaixo para realizar sua doação.
 
É rápido, fácil e indolor :)
 

Estatísticas em tempo real

Visite Nosso Patrocinador