Nenhum comentário Download


Delphi – Parte VII

 

Vimos no artigo anterior, a última parte sobre banco de dados. Claro, temos muitos assuntos ainda a serem abordados, mas acredito que com o que foi mostrado na nossa série, você terá uma grande base para começar a desenvolver aplicações cliente/server.

 

Neste artigo vamos conhecer um pouco sobre orientação a objetos no Delphi. Já aviso que o artigo não tem por objetivo explicar os conceitos em sua totalidade da programação orientada a objetos. Uma pesquisa rápida, muito será encontrado sobre o assunto na rede.

 

Aqui, vou mostrar as facilidades que o Delphi tem ao usar POO desde a sua primeira versão. E nada mais simples do que mostrar POO no Delphi com a herança visual de formulários. Claro, se você deseja usar POO em classes, interfaces, o Delphi disponibiliza tudo isso. Acredito que herança visual possa ser mais prática para visualizarmos a POO.

Entendendo OO

A ideia aqui é criar um formulário base de cadastro. Nesse formulário teremos componentes e métodos comuns para um cadastro da aplicação. Nos formulários herdados, vamos implementar os métodos necessários para o cadastro especifico.

 

Nos artigos anteriores, quando mostramos sobre banco de dados, foi trabalhoso criar um formulário para cada cadastro da nossa aplicação. Com herança visual, esse trabalho fica apenas na criação do formulário base, nos outros formulários precisamos apenas herdar e adicionar os controles de tela necessário, assim como implementar métodos.

 

O conceito disso também se aplica a classes, onde podemos ter uma classe base e outras que de acordo com suas características, herdam da primeira e implementam métodos ou propriedades para sua necessidade.

Um exemplo simples. Temos uma classe chamada “Veiculo”, mostrada no código da Listagem 1.

 

Listagem 1. Classe de exemplo

unit uVeiculo;

 

interface

 

uses

  Classes;

 

type

  TVeiculo = class

 

  private   

  public

    FCor: string;

    procedure Ligar; virtual;

    procedure Desligar; virtual;

end;

 

implementation

 

{ TVeiculo }

 

procedure TVeiculo.Desligar;

begin

  //implementado na classe herdada

end;

 

procedure TVeiculo.Ligar;

begin

  //implementado na classe herdada

end;

 

end.

 

Nessa classe, temos os métodos e propriedades comuns ao tipo Veiculo. Note que colocamos a diretiva virtual no método para que ele possa ser implementado na classe herdada. Agora precisamos implementar classes mais especificas, por exemplo, Carro e Avião, como podemos na Listagem 2.

 

Listagem 2. Novas classes que herdam de Veiculo

unit uCarro;

 

interface

 

uses

  Classes, uVeiculo;

 

type

  TCarro = class(TVeiculo)

 

  private

    FQuantidadePortas: integer;

 

  public

    procedure Ligar; override;

    procedure Desligar; override;

 

    procedure TrocarMarcha;

end;

 

{ TCarro }

 

implementation

 

procedure TCarro.Desligar;

begin

  inherited;

  //implementação para o método

end;

 

procedure TCarro.Ligar;

begin

  inherited;

  //preenchendo váriaveis no método virtual

  FQuantidadePortas := 4;

end;

 

procedure TCarro.TrocarMarcha;

begin

  if FCor <> '' then

  begin

     //implementação para o método

  end;

end;

end.

 

unit uAviao;

 

interface

 

uses

  Classes, uVeiculo;

 

type

  TAviao = class(TVeiculo)

 

    private

      FQuantidadeTurbinas: integer;

 

    public

      procedure Ligar; override;

      procedure Desligar; override;

 

      procedure LevantarVoo;

  end;

 

implementation

 

{ TAviao }

 

procedure TAviao.Desligar;

begin

  inherited;

  //implementação para o método

end;

 

procedure TAviao.LevantarVoo;

begin

  if FQuantidadeTurbinas = 2 then

  begin

     //implementação para o método

  end;

end;

 

procedure TAviao.Ligar;

begin

  inherited;

  FCor := '';

end;

 

end.

 

Note que os métodos Ligar e Desligar foram implementados para que executem funcionalidades de acordo com o seu tipo especifico de veículo. Além disso, criamos propriedades especificas para seu tipo, como QuantidadePortas (Carro).

 

Veja que as propriedades da classe Veiculo, podem ser acessadas e preenchidas pelas classes que herdam da mesma. A ideia de herança visual é bastante semelhante como veremos no decorrer do artigo.

Formulário base

Nosso formulário base terá a estrutura e controles de tela igual ao da Figura 1.

 

 

Figura 1. Formulário base da aplicação

 

Criamos o mesmo com abas para separar a parte dos controles de cadastro para a parte de pesquisa. Sim, nosso cadastro será parametrizado para que nossa aplicação tenha performance como já vimos na parte de banco de dados.

 

Agora vamos entender o código por trás do formulário de cadastro. Primeiramente, temos em tela um DataSource. Nele termos uma propriedade chamada DataSet, que será preenchida por qualquer controle que herde da classe TDataSet.

 

Assim, componentes como ClientDataSet, Table etc podem ser vinculados ao DataSource. Com isso, não sabemos no formulário base qual o controle vinculado, mas sabemos de quem ele herda, então podemos usar seus métodos, como Insert, Delete, Post etc.

 

Isso facilita a utilização dos métodos no formulário base, sem a necessidade de termos a chamada desses métodos no formulário herdado. Veja na Listagem 3 o código dos botões Novo, Salvar e Excluir implementado no formulário base, sem a necessidade de replicar nos formulários herdados.

 

Listagem 3. Métodos do formulário base

{ novo registro}

dsCadastro.DataSet.Insert;

 

{ deleta se houver dados no DataSet }

if (dsCadastro.DataSet.Active) and (dsCadastro.DataSet.RecordCount > 0) then

begin

  if MessageDlg('Tem certeza que deseja excluir o registro ?',

    mtConfirmation, [mbYes,mbNo], 0) = mrYes then

  begin

     try

        dsCadastro.DataSet.Delete;

        MessageDlg('Registro excluído com sucesso', mtInformation,

          [mbYes], 0);

     except

        On E: Exception do

        MessageDlg(E.Message, mtError, [mbOK], 0);

     end;

  end;

end;

 

{ salvo as dados se os campos estiverem preenchidos}

if CamposObrigatorios(dsCadastro) and

  (dsCadastro.State in [dsEdit, dsInsert]) then

begin

   try

      dsCadastro.DataSet.Post;

      MessageDlg('Registro salvo com sucesso', mtInformation, [mbOk], 0);

   except

      On E: Exception do

      MessageDlg(E.Message, mtError, [mbOK], 0);

   end;

end;

 

Note que usamos um bloco try...exception para emitir uma mensagem de erro caso algum problema ocorra. Outra funcionalidade interessante que podemos usar com o DataSource é para campos obrigatórios. Vamos entender um pouco. Os controles de tela são os da aba DataControls, que possuem uma propriedade DataSource.

 

Assim, podemos percorrer o DataSet vinculado ao DataSource e verificar que campos são obrigatórios (através da propriedade Required) e se os mesmos estão preenchidos. Com isso, podemos mostrar uma mensagem mais amigável ao usuário ao invés da validação feita pelo ClientDataSet por exemplo.

Veja o código na Listagem 4.

 

Listagem 4. Verificando quais os campos obrigatórios do cadastro

function CamposObrigatorios(DataSource: TDataSource): Boolean;

var

  i: integer;

begin

  { verifica quais os campos que estão em branco no cadastro}

  Result := True;

  for i := 0 to DataSource.DataSet.FieldCount - 1 do

  begin

    if DataSource.DataSet.Fields[i].Required then

      if (DataSource.DataSet.Fields[i].IsNull) or

        (DataSource.DataSet.Fields[i].AsString = '') then

      begin

        MessageDlg('Campo: " '+ DataSource.DataSet.Fields[i].DisplayLabel + '". Preenchimento obrigatório.', mtWarning,[mbOk], 0);

        Result := False;

        DataSource.DataSet.Fields[i].FocusControl;

        break;

      end;

  end;

end;

 

Nesse caso, temos que ter atenção, pois os campos obrigatórios devem ser configurados no DataSet (no nosso exemplo um ClientDataSet). Nesse caso, a facilidade do OO não faz tudo sozinho. Outra configuração que teremos que ter, se refere a pesquisa.

 

Vamos tomar por base aqui, que a pesquisa será feita sempre por um campo texto, assim, nosso DataSet vinculado ao DataSource que preenche o Grid, terá um parâmetro do tipo AsString. Como o DataSet não possui a propriedade Params precisamos fazer um typecast para o controle que estivermos usando, nesse caso um ClientDataSet:

 

dsPesquisa.DataSet.Close;

(dsPesquisa.DataSet as TClientDataSet).Params[0].AsString :=

   UpperCase('%' + edtPesquisa.Text + '%');

dsPesquisa.DataSet.Open;

 

Após o usuário achar seu registro a ser alterado, ele vai dar um duplo clique no grid de pesquisa e deseja que os dados sejam mostrados na aba de cadastro. Para isso, precisamos implementar a filtragem em cada formulário?

 

Negativo. Como sabemos o registro selecionado pelo usuário no Grid, podemos pegar o valor da chave e repassar para o DataSet que faz a filtragem do mesmo. Claro, nosso DataSet (no nosso exemplo um ClientDataSet) precisa ter como parâmetro de filtragem um tipo AsInteger (padrão para chaves).

 

Veja na Listagem 5 o código que filtra o registro do DataSet baseado na escolha do usuário no Grid.

 

Listagem 5. Filtragem do registro selecionado pelo usuário no Grid

procedure TfrmCadastro.DBGrid1DblClick(Sender: TObject);

begin

  { filtra os dados e mostra a aba de dados cadastrais }

  dsCadastro.DataSet.Close;

  (dsCadastro.DataSet as TClientDataSet).Params[0].AsInteger :=

     dsPesquisa.DataSet.Fields[0].AsInteger;

  dsCadastro.DataSet.Open;

  dsCadastro.DataSet.Edit;

 

  if (dsCadastro.DataSet.RecordCount > 0) then

    PageControl1.ActivePage := tabDados;

end;

 

Caso algum registro seja selecionado, mostramos a aba de cadastro. No formulário base implementamos um fluxo para o cadastro, por exemplo: quando o usuário clicar em Excluir, temos que emitir uma mensagem de confirmação e somente assim, excluir o registro (como podemos ver na Listagem 4).

 

Além disso, precisamos limpar os controles de tela caso não utilizarmos os controles data-aware. Assim, no formulário base esse processo todo é implementado, não precisando replicar o mesmo no formulário herdado.

 

O mesmo se aplica para o Salvar. O sistema fará a validação dos campos obrigatórios e chamara o salvar o DataSet. Se nenhum erro ocorreu, uma mensagem será mostrada ao usuário. Novamente, isso apenas é implementado no formulário base, sem replicação.

 

Com esses códigos mostrados até o momento, para criar um cadastro totalmente funcional, basta herdar o formulário base, colocar os controles de tela e configurar corretamente os DataSources e poderíamos utilizar o mesmo sem problemas.

Criando o formulário herdado

Isso vamos fazer agora. Acesse o menu File>New>Other. No editor, escolha o item Inheritable Items e escolha o formulário base (Figura 2)

 

 

Figura 2. Criando o formulário herdado

Ao clicar em OK um novo formulário será adicionado ao projeto. Os controles de tela são os mesmos que estão presentes no formulário base. Salve o formulário. Agora precisamos configurar os controles DataSets e adicionar os controles data-aware.

 

Vincule o dsCadastro e dsPesquisa para respectivamente: DM.cdsCliente e DMPesquisa.cdsPesquisaCliente. Adicione os componentes necessários para o cadastro de cliente (Figura 3).

 

 

Figura 3. Adicionando os controles de tela no formulário herdado.

 

Faça a chamado ao novo formulário, no lugar do anterior. Execute a aplicação, veja que o cadastro esta pronto, basta acessar a aba Pesquisar e realizar a pesquisa (Figura 4).

 

 

Figura 4. Pesquisa funcionando no cadastro de clientes

 

Dê um duplo clique em qualquer registro. Cadastro foi filtrado corretamente (Figura 5).

 

 

Figura 5. Filtragem do cliente baseado na escolha na aba Pesquisar

 

Experimente alterar e salvar o registro. Será indicada a mensagem que a funcionalidade foi realizada com sucesso. Agora vem o melhor de tudo. Acesse o editor de código do formulário. Não escrevemos nenhuma linha de código (Figura 6).

 

 

Figura 6. Nenhuma linha de código presente no formulário herdado

Temos apenas a declaração dos controles de tela e no uses os Data Modules que usamos. Isso tudo graças ao OO, pois herdamos de uma classe (TForm) todas as suas funcionalidades e configuramos as propriedades necessárias (ClientDataSets).

 

Claro, nem todos os cadastros serão assim, para alguns será necessário customizar. Nesse caso, basta declarar métodos nos formulários herdados (esses são os métodos específicos de cada classe) para serem utilizados somente nesses formulários.

 

Um exemplo simples para um método virtual, que é declarado na classe base e implementado nas classes que herdam. Um método para aplicar permissões de usuários no cadastro. O método pode ser declarado no formulário base. Pode até mesmo ser “chamado” no fluxo que temos, mas sua implementação será feita no formulário herdado.


Ele será redeclarado e sua implementação será de acordo com o formulário que estamos trabalhando. Isso é simples. Crie os outros cadastros da aplicação. Você não irá demorar 5 minutos para fazer isso.

Conclusões

Vimos nesse artigo, um exemplo simples de utilização de programação orientada a objetos, usando herança visual de formulários. Não quis mostrar toda a teoria da orientação a objetos. Para aprender isso, basta uma pequena pesquisa na internet.

 

Com esse artigo, quis mostrar toda a facilidade que o Delphi nos proporciona para criarmos aplicações modernas e com muita produtividade.

Um grande abraço a todos e até a próxima!