Cleverton Bueno
← Voltar para todos os artigos

O Framework é Meu:
Como Criei meu Próprio 'Hibernate' em C++ Builder para Dominar a Manutenção

Quem programa há algum tempo, especialmente em C++ Builder (VCL), conhece 'A Dor'. Você cria seu primeiro formulário de cadastro, arrasta 50 componentes e começa o trabalho braçal. Eu me recusei a continuar assim e criei uma solução.

E aí, pessoal. A dor é real. Você cria um cadastro de clientes, valida campo por campo, monta SQL na mão, concatena strings...

A Dor: O Pesadelo do "Código Espaguete"

No botão "Gravar" (B_GravClick), você começa a escrever aquele código que todo mundo já viu (e sofreu):

C++: O Jeito "Ruim" (Trabalho Braçal)
void __fastcall TFormCliente::B_GravClick(TObject *Sender)
{
    // Primeiro, valida campo por campo
    if (E_Raza->Text.IsEmpty()) {
        ShowMessage("Razão Social é obrigatória!");
        return;
    }
    // ...mais 30 'if's ...

    // Agora, montar o SQL...
    AnsiString sql = "UPDATE CLIENTES SET "
                   + "RAZAO = '" + E_Raza->Text + "', "
                   + "CNPJ = '" + E_Cgc->Text + "', "
                   + "WHERE IDCLIENTE = " + E_Codi->Text;
    
    DadosBD->QueryOperacao->SQL->Add(sql);
    DadosBD->QueryOperacao->ExecSQL();
}

E não para aí. Você tem que fazer o INSERT, a função de Leitura, a de Limpar, a de Bloquear... Você se vê fazendo COPIA E COLA desse "código espaguete" em dezenas de formulários. Quando o cliente pede para adicionar um novo campo, é o caos. Isso não é engenharia.

A "Virada de Chave": O E Se?

Eu parei e pensei. O problema é sempre o mesmo. O que muda são só os nomes dos campos e das tabelas. E se o meu formulário (TForm) fosse "burro"? E se ele não soubesse NADA sobre o banco de dados? Foi aí que nasceu a minha cbslib (minha biblioteca para manipulação de banco de dados).

O "Contrato": struct estrutura (O Mapa de Metadados)

O primeiro passo foi criar o "mapa". Em estrutura.h, eu defini o coração de todo o meu sistema, a struct estrutura. Isso é um ORM (Mapeamento Objeto-Relacional) na sua forma mais pura.

C++: O Contrato de Mapeamento (estrutura.h)
struct estrutura {
    char banco[40];       // NOME DO CAMPO NO BANCO (ex: "ba")
    char campo[40];       // NOME DO COMPONENTE NA TELA (ex: "E_Raza")
    char tipocampo[10];   // TIPO DE COMPONENTE (ex: "Edit", "Radio")
    char tipobanco[10];   // TIPO DE DADO NO BANCO (ex: "int", "varchar")
    char gravar[2];       // Grava? (S/N)
    char ler[2];          // Lê? (S/N)
    char primary[2];      // É Chave Primária? (S/N)
    char podebranco[2];   // Pode ser Nulo? (S/N)
    //... e outros controles
};

Agora, para cada formulário, eu não escrevo código. Eu escrevo um "mapa". Veja o mapa do cadastro de configurações:

C++: Implementando o Mapa na Tela
struct estrutura VARIACONF[] =
// banco    campo      tipocampo  tipobanco  gravar ler primary podebranco
{"idxyza",  "E_Codi",  "Edit",    "int",     "N",   "S", "S",    "N"},
{"ba",      "E_Raza",  "Edit",    "varchar", "S",   "S", "N",    "N"},
{"da",      "E_Rece",  "radio",   "varchar", "S",   "S", "N",    "S"},
{"ae",      "E_Reme",  "Edit",    "varchar", "S",   "S", "N",    "S"},
// ... mais 50 campos ...
{"0","","","","","","","","","","","","","","",""};

Esse mapa diz tudo: Quem é Primary Key, quem pode ser branco, qual componente recebe qual dado. Com o mapa pronto, eu só precisava da "engine" para lê-lo.

A "Engine": O Coração do Framework em cbslib.c

Eu criei duas funções centrais na minha biblioteca cbslib.c: LerVar e GravaVar.

A Engine de Leitura (LerVar)

Essa função faz o "data binding" automático. Ela recebe o nome do formulário, a query já executada e o "mapa".

C++: Pseudocódigo da Engine de Leitura
void LerVar(AnsiString NForm, TADOQuery *Query, struct estrutura VARIAVEL[])
{
    // 1. Acha o formulário que me chamou
    TForm *Forminho = Screen->Forms[NForm];

    // 2. Loop no "mapa" (ex: VARIACONF)
    for (int i=0; VARIAVEL[i].banco[0] != '0'; i++)
    {
        // 3. Pega o dado do Banco e DECIFRA (Segurança Transparente)
        AnsiString valorDoBanco = Query->FieldByName(VARIAVEL[i].banco)->AsString;
        char valorDecifrado[800];
        cbs_dcript(valorDoBanco.c_str(), valorDecifrado);
        
        // 4. Acha o componente da tela PELO NOME!
        TComponent *comp = Forminho->FindComponent(VARIAVEL[i].campo);

        // 5. Popula o componente certo, do jeito certo
        if (VARIAVEL[i].tipocampo == "EDIT") {
            ((TEdit*)comp)->Text = valorDecifrado;
        }
    }
}

O que isso significa? Que o meu código de FormActivate em todas as telas de cadastro é uma única linha: LerVar("FConf", DadosBD->Query4, VARIACONF, NULL);.

A Engine de Gravação (GravaVar)

Aqui a mágica é ainda maior. Essa função constrói o SQL dinamicamente, seja INSERT ou UPDATE.

C++: Pseudocódigo da Engine de Gravação
void GravaVar(..., TADOQuery *Query, char *Banco, ..., int inclus)
{
    // Loop no mapa...
    for (int i=0; VARIAVEL[i].banco[0] != '0'; i++)
    {
        // Pega o valor da tela e CIFRA (Segurança Transparente)
        AnsiString valorDaTela = ((TEdit*)comp)->Text;
        char valorCifrado[800];
        cbs_cript(valorDaTela.c_str(), valorCifrado);

        if (inclus == 1) { // INSERT
             sql_campos += VARIAVEL[i].banco + ", ";
             sql_valores += "'" + AnsiString(valorCifrado) + "', ";
        } else { // UPDATE: Só atualiza se mudou (Change Tracking)
             if (valorDaTela != valorAntigo)
                sql_update += VARIAVEL[i].banco + " = '" + valorCifrado + "', ";
        }
    }
    // Executa SQL Dinâmico
    QOper->SQL->Add(sql_final);
    QOper->ExecSQL();
}

O Gran Finale: Segurança 100% Transparente

O leitor mais atento viu a cbs_cript e a cbs_dcript. Eu não queria senhas e dados sensíveis legíveis no banco. A minha solução foi embutir a criptografia dentro da engine.

LerVar sempre chama cbs_dcript ao ler do banco. GravaVar sempre chama cbs_cript ao pegar o dado da tela. O formulário não faz a menor ideia de que o dado está criptografado.

O Ecossistema: O Framework Completo

A beleza desse "mapa" é que ele alimenta todo o ciclo de vida do formulário, não apenas a leitura e gravação. Eu criei um ecossistema completo:

  • LimpaVar(...): Lê o mapa e limpa todos os componentes.
  • BloqueiaVar(...) / DesbloqueiaVar(...): Habilita ou desabilita componentes baseado na flag de gravação.
  • PossoGravar(...): A engine de validação. Lê o mapa, procura por podebranco == 'N' e valida automaticamente.
  • StatButton(...): Uma máquina de estado de UI para controlar botões (Gravar, Cancelar, Novo).

Conclusão

No fim, o tempo que gastei para criar o cbslib.c e o estrutura.h foi o melhor investimento do projeto. Para criar uma nova tela de CRUD, eu literalmente desenho a tela, defino o mapa e chamo as funções.

Eu não escrevo mais código para "salvar um cliente". Eu escrevo código que ensina o meu framework a salvar um cliente. Essa é a diferença entre programar e criar arquitetura de software.