Resumindo programação

Robert Gaffo (e-mail) é programador Delphi e instrutor de programação em Delphi e outras linguagens como Visual Basic, C/C++ e Java. Leciona aulas de conceitos de Banco de Dados, SQL Server, Oracle, Interbase/Firebird e MySql e dá treinamentos In Company. Trabalha com Delphi em aplicações desktop e multi-camadas.

Nosso objetivo será a identificação de pontos na programação que podem ser resumidos, desejo de programadores de qualquer linguagem. Trataremos aqui como identificar, onde podemos reduzir código e como fazer isso.

1º Exemplo: Criação de formulários

Para reduzir, primeiro observamos lugares que contém códigos repetidos para que, então, possamos criar funções ou procedures para reduzi-los. Por exemplo, veja o código abaixo:

Try
   
frmCliente := TfrmCliente.Create (self); // Criação do formulário na memória
    frmCliente.ShowModal; // Exibição para o usuário
Finally
   
FreeAndNil (frmCliente) // Libera dos recursos alocados na memória  
End;
// Try

A idéia do código acima é abrir o formulário de clientes, e usar o mesmo código para abrir os outros formulários. Como os formulários não são criados automaticamente quando o projeto é iniciado (ou seja, em Auto-create forms  que está em Project options está somente o formulário que precisa ser iniciado, os outros não), então temos que criar-los em run-time. Com o código acima é possível fazer isso.

Dessa forma, o código se torna repetitivo, pois a única coisa que mudamos nesse código é o nome do formulário, certo? Imagine se precisassemos fazer alguma manutenção nessa rotina de criação de formulário. Teríamos que fazer a manutenção em todos os lugares em que usamos esse código para criarmos os formulários. Isso se torna inviável, complicado e trabalhoso.

Para eliminarmos esse problema, será criado uma procedure para criar o formulário para nós, onde centralizaremos a criação do formulário. Usaremos procedure por que não retornará nenhum resultado, mas nada impede de criarmos uma function e retornar um valor, se foi possível ou não criar o formulário, por exemplo.

Primeiramente temos que ver o que nós precisamos para criar o formulário. No nosso caso precisamos saber qual é o formulário que será criado. Podemos saber disso através da classe do formulário que será criado. Então o código ficaria assim:

Procedure CriaForm (NomeForm: TFormClass); *  
Begin  

Try
    TForm (ClassForm) := ClassForm.Create(self); 
    TForm (ClassForm).ShowModal;  
Finally
    FreeAndNil(ClassForm);  
End
; // Try  
end
; // CriaForm

Para chamar essa procedure, digite o código abaixo:

CriaForm (TfrmCliente);

Agora, se precisassemos fazer alguma manutenção, seria em somente um local e isso se refletiria para todos os outros formulários.

Entendendo o código

A parâmetro ClassForm representa a classe do formulário que desejamos, tendo a classe do formulário a ser criada podemos obter todas as informações que quisermos desse formulário, como os objetos que estão no formulário por exemplo.

TForm (ClassForm) é um casting1 da classe passada por parâmetro que permite sabermos o nome do formulário. ClassForm.Create (self) é a criação do formulário em memória. TForm (ClassForm).ShowModal exibe o formulário do tipo modal para o usuário e FreeAndNil (ClassForm) é a destruição do formulário, ou seja, é a liberação dos recursos usados pelo formulário na memória.

Comparando esse código com o anterior, veremos que é a mesma coisa, com a diferença de que não precisaremos digitar várias vezes o mesmo código e o mesmo ficar centralizado. Agora é só chamar a mesma procedure com o nome dos outros formulários.

2º Exemplo: DBGrid Zebrado

Para deixarmos um DBGrid, zebrado, temos que programar no evento OnDrawColumnCell, ou seja, quando estiver sendo desenhado o campo da tabela na célula, e toda vez que precisar desenhar uma célula, esse evento é chamado. Digite o seguinte no evento:

procedure TfrmCliente.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);  
begin  
if not
(Odd(DataSet.RecNo)) then
// Se não for par 
    if not (gdSelected in State) then Begin
// Se o estado da célula não é selecionado 
        with DBGrid1 do Begin 
            with
Canvas do Begin 
   
             Brush.Color := clMenu;
// Cor da célula
                 FillRect (Rect);
// Pinta a célula 
            End;
// with Canvas
             DefaultDrawDataCell (Rect, Column.Field, State)
// Reescreve o valor que vem do banco
        End
// With DBGrid1 
    End
// if not (gdSelected in State)
End;
 

Dessa forma temos um DBGrid zebrado, mas se quisermos que os outros DBGrids também sejam zebrados temos que implementar o mesmo código para todos, assim manteríamos um padrão. Vamos resumir esse código também para não ter que ficar digitando todo esse código para os outros DBGrids. Vamos criar a procedure abaixo:

Procedure GridZebrado (RecNo : LongInt; Grid : TDBGrid; Rect : TRect; Column : TColumn; State : TGridDrawState); *  
Begin  
if not
(Odd(RecNo)) then
// Se não for par 
    if
not (gdSelected in State) then Begin // Se o estado da célula não é selecionado
   
     with Grid do Begin  
            with Canvas do Begin
                Brush.Color := clMenu;
// Cor da célula
                FillRect (Rect);
// Pinta a célula
   
         End; // with Canvas
            DefaultDrawDataCell (Rect, Column.Field, State)
// Reescreve o valor que vem do banco
        End // With Grid
    End //
if not (gdSelected in State)

End

Para chamar essa procedure, digite o código abaixo:

GridZebrado (TDBGrid(Sender).DataSource.DataSet.RecNo, TDBGrid(Sender), Rect, Column, State);

Entendendo o código

Criamos uma procedure que recebe como parâmetros os mesmos parâmetros que o evento DrawColumnCell possui quando vamos programar nele, acrescidos do número do registro atual e qual DBGrid ficará com o efeito. Dessa forma se pode fazer o efeito em qualquer DBGrid. O parâmetro RecNo é o número do registro atual do DataSet, Grid é o nome do DBGrid que desejamos aplicar o efeito zebrado.

Rect são as coordenadas da célula que está sendo desenhada. Column representa o campo da tabela. State é o estado da célula que indica se ela está selecionada, somente com o foco ou com uma região fixa do DBGrid.

Usamos o método Canvas do DBGrid para mudar a cor da célula onde Brush.Color indica a cor da célula e FillRect (Rect) pinta a célula com a cor escolhida baseado nas coordenadas da célula atual. Quando é executado o método FillRect (Rect), o valor do campo da tabela é apagado do DBGrid, então usamos o método DefaultDrawDataCell (Rect, Column.Field, State) informando as coordenadas da célula, o campo da tabela e o estado atual da célula para que ele coloque o valor novamente no DBGrid.

Poderíamos ter criado um parâmetro em que passássemos a cor que seria usado para pintar, mas para manter um padrão foi implementado a cor na procedure mesmo. Agora faça o mesmo para os outros DBGrids, não se esqueçendo que você pode associar o evento DrawColumnCell dos outros DBGrids com o evento  DrawColumnCell do DBGrid que você chamou a procedure GridZebrado.

3º Exemplo: Instruções SQL

Em uma aplicação é comum usarmos instruções SQL de qualquer tipo. A idéia desse exemplo é mostrar como geralmente são feitas as instruções e como poderíamos melhorá-las. Podemos dividir as instruções SQL em SELECT e as outras, porque quando executamos instruções SELECT queremos ver os dados já as outras como DELETE ou UPDATE são usadas para serem executadas no banco de dados sem trazer nenhuma resposta.

Vamos imaginar que temos no nosso formulário um Edit chamado edtNome onde o usuário digita o nome de um cliente para fazer uma busca. Quando o usuário clica no botão pesquisar é executado o código abaixo.

Instrução SELECT

With cdsCustomer do Begin 
    Close;
    CommandText := "select * from customer where CUSTOMER like " + QuotedStr (edtNome.Text + ‘%");
    Open;
End; //
with

E toda vez que fossemos executar uma instrução faríamos da mesma forma. Fechamos o DataSet, colocamos a instrução que queremos executar e abrimos o DataSet novamente. Se precisássemos fazer isso em vários lugares, estaríamos duplicando código. É mais fácil criamos uma procedure ou uma function para resumir esse código deixando-o também centralizado. No nosso caso vamos criar uma function, pois dessa forma podemos retornar se a instrução foi ou não executada corretamente. A função é a seguinte:

function SelectSQL(qry: TClientDataSet; Instr: String): Boolean; *
begin
Try
    qry.Close; 
    qry.CommandText := Instr;
    qry.Open;
    Result := True
Except
    Result := False
End; // Try
end;

Para chamar essa função digite o código abaixo:

SelectSQL(ClientDataSet1, "select * from customer where CUSTOMER like " + QuotedStr (Edit1.Text + "%"))

Entendendo o código

Criamos dois parâmetros, um chamado qry do tipo TClientDataSet, pois, nosso DBGrid está ligado no DataSource, e o DataSource está ligado no ClientDataSet. E outro parâmetro chamado Instr do tipo String, que é a instrução SELECT que será disparada no banco. Fazemos a mesma coisa que tínhamos feito no botão. Abaixo do qry.Open colocamos o resultado da função que é verdadeiro, ou seja, conseguiu executar a instrução. O código é colocado dentro de um bloco protegido Try Except End; Caso ocorra qualquer tipo de falha, ele vai para a parte do Except e o resultado da função é Falso, ou seja, não conseguiu executar a instrução SQL.

Outras instruções SQL

Como as outras instruções SQL não retornarão dados, podemos generalizar uma função para todas elas. Não importa se ele for UPDATE, DELETE, INSERT ou qualquer outra. Geralmente implementamos a rotina para fazer isso da mesma forma que fizemos para a instrução SELECT acima, com a diferença de trocarmos o método OPEN para o método EXECSQL. 

Vamos imaginar que no nosso formulário tem dois Edits, um chamado edtDe e o outro edtPara, onde o usuário digita o nome do contato atual e para qual nome ele quer alterar e um botão Atualizar. Vamos ver como a função vai ficar abaixo:

function ExecuteSQL(conn : TSQLConnection; Instr: String): Boolean; *  
var  
    qry : TSQLDataSet
begin  
Try  
    Try
       
qry := TSQLDataSet.Create(self);
        qry.SQLConnection := conn;
        qry.CommandType := ctQuery;
        qry.CommandText := Instr;
        qry.ExecSQL;
        Result := True
    Except
// Se ocorrer uma exceção  
         Result := False
    End; // Try  
Finally
// É executado independente do que aconteça ao código acima
    FreeAndNil(qry);
// Libera o objeto da memória
End;
end;

Para chamar essa função, digite o código abaixo no botão atualizar:

ExecuteSQL(SQLConnection1, "update customer set CONTACT_FIRST = " + QuotedStr(edtPara.Text) + " where CONTACT_FIRST = " + QuotedStr(edtDe.Text));

Entendendo o código:

Como os componentes usados no exemplo são do DBExpress, foi criado um parâmetro chamado conn para indicar qual o SQLConnection que está ligado no banco. Precisamos desse parâmetro por que em run-time é criado um objeto do tipo TSQLDataSet para executar a instrução SQL, foi criado um outro parâmetro chamado Instr que é a instrução SQL que será executado no banco de dados.

O primeiro Try faz parte da estrutura Try Finally End. Essa estrutura é definida como proteção de recursos, pois, independente se ocorrer ou não uma exceção, essa parte será executa permitindo, por exemplo, voltar um componente ao seu estado inicial ou liberar um objeto da memória como estamos fazendo aqui. O segundo Try faz parte da estrutura Try Except End. Sempre que ocorrer uma exceção entre Try e Except, ele pula para a parte depois do Except executando o tratamento. Nós fazemos isso para indicar que uma instrução não pôde ser executada, ou seja, Result := False.

Conclusão

Analisando lugares na programação onde o código se repete, podemos criar procedures ou functions para não ficar repetindo todo o código. Dessa forma, sempre que incluirmos algo novo no programa e precisar usar aquele código, é só chamar a procedure ou a function passando os parâmetros necessários para o bloco de código ser executado.

Fontes

Os fontes que vem com esse artigo usam conexão usando DBExpress com o Interbase. O banco usado está na pasta C:\Arquivos de programas\Arquivos comuns\Borland Shared\Data” que vem junto com o Delphi quando ele é instalado e se chama Employee.gdb e a tabela é Customer.

Fonte

Notas: 

* É necessário que se declare a assinatura das procedures ou as functions que nós criamos aqui. Você pode declará-las antes da seção Private ou antes de Implementation. Copie a assinatura que está na linha antes do asterisco e cole em umas das sessões descritas acima. TypeCasting é a transformação de um tipo para outro.