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):
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.
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:
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".
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.
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.