|
GRUPOS DE DISCUSSÃO PROFISSIONAL WINDEV, WEBDEV e WINDEV Mobile |
| | | | | |
| Inicio → WINDEV 25 → Exemplo de Oop construtor de objetos dinâmicos a partir de uma classe genérica |
| Exemplo de Oop construtor de objetos dinâmicos a partir de uma classe genérica |
| Iniciado por Boller, jul., 13 2025 7:02 PM - Sem resposta |
| |
| | | |
|
| |
Membro registado 4.618 mensagems |
|
| Publicado em julho, 13 2025 - 7:02 PM |
Bom dia
A seguir exemplo postado pelo sr Oliver Dawson

Esse código está escrito em WLanguage, a linguagem usada no WinDev/WebDev/WinDev Mobile (WX) da PC SOFT. Ele define um construtor de objetos dinâmicos a partir de uma classe genérica (vClasse), fazendo mapeamento de atributos e preenchendo uma coleção com instâncias baseadas em registros lidos de um arquivo HFSQL.
⸻
✅ Resumo do que o código faz:
A procedure Constructeur(vClasse): 1. Cria dinamicamente uma instância de uma classe vClasse. 2. Obtém a definição da estrutura da classe com RécupèreDéfinition. 3. Verifica quais atributos da classe são chaves únicas. 4. Se encontrar uma chave única, ela: • Mapeia os registros de um arquivo (tabela HFSQL). • Para cada registro, cria uma nova instância da classe (vClasse) com o valor da chave. • Adiciona essas instâncias a uma coleção _m_tabCollection.
⸻
🔍 Etapas detalhadas:
1. Inicialização
pclObjetDemande est un objet dynamique defClasse est une Définition descCle est une Description de Variable
• pclObjetDemande: objeto que será construído. • defClasse: definição da classe (obtida dinamicamente). • descCle: descrição da variável que pode ser uma chave única.
2. Alocação e definição
pclObjetDemande <- allouer un vClasse defClasse = RécupèreDéfinition(pclObjetDemande)
• Instancia dinamicamente vClasse. • Recupera sua definição com RécupèreDéfinition.
3. Identificação da chave única
TANTQUE nIndiceVar <= nOccurrenceVar ET bCleUnique = Faux descCle = defClasse.Variable[nIndiceVar] bCleUnique = descCle.AttributCléUnique FIN
• Percorre todos os atributos da classe até encontrar um com AttributCléUnique = Vrai.
4. Se tiver chave única…
SI bCleUnique = Vrai ALORS sCleUnique = descCle.AttributMapping
• Se encontrou a chave, obtém o nome do campo no banco correspondente.
5. Percorre todos os registros do arquivo
HLitPremier({sFichier,indFichier}) TANTQUE ... PAS HEnDehors({sFichier,indFichier}) _m_tabCollection.Ajoute(allouer un vClasse(...)) HLitSuivant({sFichier,indFichier}) FIN
• Percorre todos os registros (HLitPremier a HLitSuivant). • Para cada um: • Cria dinamicamente uma nova instância de vClasse. • Adiciona na coleção _m_tabCollection.
⸻
📦 Exemplo de uso prático:
Se você tiver uma classe Cliente com a propriedade CPF como chave única e um arquivo HFSQL com os registros dos clientes, este código irá: • Mapear cada registro do arquivo Cliente. • Criar uma instância da classe Cliente para cada linha. • Armazenar essas instâncias na _m_tabCollection.
⸻
🔐 Pontos fortes do código: • Reutilizável para qualquer classe (vClasse). • Usa introspecção (com RécupèreDéfinition) para trabalhar dinamicamente com a estrutura da classe. • Automatiza a criação de objetos a partir dos dados no banco.
⸻⸻
✅ Código Refatorado em WLanguage (WX) - Tradução e Melhoria
// ╔════════════════════════════════════════════════════╗ // ║ PROCEDURE ConstrutorClasse(vClasse) // ║ Finalidade: Criar objetos dinamicamente a partir // ║ de registros em arquivo, utilizando mapeamento // ║ por chave única. // ╚════════════════════════════════════════════════════╝
PROCEDURE ConstrutorClasse(vClasse)
objSolicitado é um objeto dinâmico defClasse é uma Definição descChave é uma Descrição de Variável
indiceVar é um inteiro = 1 totalVariaveis é um inteiro possuiChaveUnica é um booleano = Falso
nomeArquivo é uma cadeia nomeChaveUnica é uma cadeia
// Aloca dinamicamente um objeto da classe objSolicitado <- Alocar(vClasse)
// Recupera a definição da classe defClasse = RecuperaDefinicao(objSolicitado)
// Recupera o nome do arquivo físico associado à classe nomeArquivo = defClasse.AtributoMapping
// Verifica a quantidade de atributos na classe totalVariaveis = defClasse.Variavel.Ocorrencia
// Procura por um atributo que seja chave única ENQUANTO indiceVar <= totalVariaveis E possuiChaveUnica = Falso descChave = defClasse.Variavel[indiceVar] possuiChaveUnica = descChave.AtributoChaveUnica indiceVar += 1 FIM
// Se encontrou uma chave única, faz o mapeamento dos dados SE possuiChaveUnica = Verdadeiro ENTÃO
// Obtém o nome do campo da chave única mapeado no arquivo nomeChaveUnica = descChave.AtributoMapping
// Inicia leitura do primeiro registro do arquivo HLitPrimeiro({nomeArquivo, indArquivo})
// Enquanto houver registros no arquivo ENQUANTO NÃO HEnDehors({nomeArquivo, indArquivo}) // Adiciona dinamicamente um novo objeto à coleção com base na chave única _m_tabColecao.Adiciona( Alocar(vClasse({ConstruirCadeia("%1.%2", nomeArquivo, nomeChaveUnica), indCampo})) )
// Lê o próximo registro HLitSeguinte({nomeArquivo, indArquivo}) FIM FIM
⸻
🧠 Mudanças realizadas:
Original (Francês) Refatorado (Português) Observação Constructeur ConstrutorClasse Nome mais descritivo pclObjetDemande objSolicitado Nome limpo e direto defClasse (mantido) Já é autoexplicativo descCle descChave Traduzido bCleUnique possuiChaveUnica Melhor semântica sFichier nomeArquivo Mais intuitivo sCleUnique nomeChaveUnica Nome real do campo _m_tabCollection.Ajoute _m_tabColecao.Adiciona Padrão brasileiro
⸻
🚀 Sugestões futuras (opcionais): 1. Suporte a múltiplas chaves primárias compostas. 2. Adição de log em caso de erro com Trace() ou Info(). 3. Exportação da _m_tabColecao para JSON/XML. 4. Criação de um método estático .CarregarDeArquivo() no vClasse para encapsular esse comportamento.
⸻
Perfeito, Adriano! Aqui está a versão refatorada e traduzida para inglês da procedure ConstrutorClasse, com nomes padronizados, estrutura limpa e alinhada com as WX Best Practices, incluindo notação clara e comentários descritivos.
⸻
✅ Refactored Procedure in WLanguage (English Version)
// ╔══════════════════════════════════════════════════════════════╗ // ║ PROCEDURE BuildClassInstance(vClass) // ║ Purpose: Dynamically builds objects from a file based on // ║ class definition, using the unique key as reference. // ╚══════════════════════════════════════════════════════════════╝
PROCEDURE BuildClassInstance(vClass)
dynObject is dynamic object classDef is Definition varDesc is Variable Description
varIndex is int = 1 varCount is int hasUniqueKey is boolean = False
fileName is string uniqueKeyName is string
// Dynamically allocate the requested class dynObject <- Allocate(vClass)
// Retrieve the class definition classDef = GetDefinition(dynObject)
// Get the physical file name from the mapping fileName = classDef.AttributeMapping
// Get the number of variables in the class varCount = classDef.Variable.Occurrence
// Search for a variable marked as a unique key WHILE varIndex <= varCount AND hasUniqueKey = False varDesc = classDef.Variable[varIndex] hasUniqueKey = varDesc.AttributeUniqueKey varIndex += 1 END
// If a unique key was found, load records IF hasUniqueKey = True THEN
// Get the mapped name of the unique key attribute uniqueKeyName = varDesc.AttributeMapping
// Start reading the first record HReadFirst({fileName, fileIndex})
// While not at the end of the file WHILE NOT HOut({fileName, fileIndex})
// Add a new instance of the class to the collection using the unique key _m_tabCollection.Add( Allocate(vClass({BuildString("%1.%2", fileName, uniqueKeyName), fieldIndex})) )
// Read the next record HReadNext({fileName, fileIndex}) END END
⸻
🧠 Key Translation & Refactoring Points
Original (Português) English Refactor Description ConstrutorClasse BuildClassInstance Clear intent in English objSolicitado dynObject Short for dynamic object descChave varDesc Variable description possuiChaveUnica hasUniqueKey Boolean clarity nomeArquivo fileName Common data file name nomeChaveUnica uniqueKeyName Mapped key name from definition _m_tabColecao _m_tabCollection Standard naming ConstruirCadeia BuildString English analog to string format
⸻
📦 Bonus Suggestions (for scalability): • Use Trace() or Error() to log if no unique key is found. • Allow override of fileIndex, fieldIndex via parameters. • Return the populated collection (RESULT _m_tabCollection). • Convert _m_tabCollection to JSON: _m_tabCollection.ToJSON().
⸻
Excelente, Adriano. Abaixo está uma proposta de extensão evoluída do construtor de objetos WX, que:
⸻
✅ Implementa JOIN dinâmico entre múltiplas tabelas,
✅ Otimiza com cache em memória,
✅ Mantém compatibilidade com sua SuperClasseWX e diretrizes avançadas.
⸻
🧠 Objetivo
Permitir que, ao construir dinamicamente uma coleção de objetos, sejam feitos JOINs entre tabelas relacionadas (ex: Cliente + Endereço + Cidade), mantendo performance com cache em memória, reaproveitamento de estrutura de classe e leitura unificada.
⸻
📐 Estrutura da Solução
|--> BuildClassWithJoin(vClasse, sJoinSpec, bUsaCache) | |--> Recupera definição da classe principal |--> Analisa string de JOIN (sJoinSpec) |--> Monta consulta composta (JOIN SQL ou leitura manual) |--> Se bUsaCache = Vrai | |--> Verifica cache em memória (_m_cache) | |--> Se já carregado, retorna direto |--> Caso contrário: |--> Lê dados (com HExecuteSQLQuery ou HRead + chave estrangeira) |--> Cria instâncias unificadas |--> Armazena no _m_tabCollection e no _m_cache
⸻
🧱 Proposta de Implementação (WLanguage)
🔧 Procedure: BuildClassWithJoin
PROCEDURE BuildClassWithJoin(vClasse, sJoinSpec = "", bUsaCache = Vrai)
objInstancia é objeto dinâmico defClasse é Definição descChave é Descrição de Variável
filePrincipal é cadeia keyPrincipal é cadeia sqlJoin é cadeia hashCache é cadeia
_m_cache é um mapa de objetos dinâmicos
// Aloca classe e obtém definição objInstancia <- Alocar(vClasse) defClasse = RecuperaDefinicao(objInstancia) filePrincipal = defClasse.AtributoMapping
// Verifica chave primária indice é int = 1 chaveUnicaEncontrada é booleano = Falso
ENQUANTO indice <= defClasse.Variavel.Ocorrencia E chaveUnicaEncontrada = Falso descChave = defClasse.Variavel[indice] chaveUnicaEncontrada = descChave.AtributoChaveUnica indice += 1 FIM
SE chaveUnicaEncontrada = Falso ENTÃO Info("Nenhuma chave única encontrada na classe.") RETORNA FIM
keyPrincipal = descChave.AtributoMapping
// Gera hash para controle de cache hashCache = HashMD5(filePrincipal + sJoinSpec)
// Verifica cache SE bUsaCache E Existe(_m_cache[hashCache]) ENTÃO _m_tabCollection = _m_cache[hashCache] RETORNA FIM
// JOIN manual entre arquivos _m_tabCollection = []
// Se sJoinSpec vazio, leitura padrão SE sJoinSpec = "" ENTÃO
HLitPrimeiro({filePrincipal, indArquivo}) ENQUANTO NÃO HEnDehors({filePrincipal, indArquivo})
_m_tabCollection.Adiciona(Alocar(vClasse({filePrincipal + "." + keyPrincipal, indCampo}))) HLitSeguinte({filePrincipal, indArquivo}) FIM
SENÃO // JOIN manual usando múltiplos arquivos
// sJoinSpec = "Endereco.IDCliente=Cliente.ID;Cidade.ID=Endereco.IDCidade" arrJoin é array de cadeia = Divide(sJoinSpec, ";")
HLitPrimeiro({filePrincipal, indArquivo}) ENQUANTO NÃO HEnDehors({filePrincipal, indArquivo})
// Aloca classe principal objPrincipal <- Alocar(vClasse({filePrincipal + "." + keyPrincipal, indCampo}))
// Para cada JOIN: executa HReadSeek no arquivo relacionado PARA CADA sJoin IN arrJoin
chaveOrigem é cadeia = ExtraireAvant(sJoin, "=") chaveDestino é cadeia = ExtraireApres(sJoin, "=")
arqOrigem é cadeia = ExtraireAvant(chaveOrigem, ".") campoOrigem é cadeia = ExtraireApres(chaveOrigem, ".")
arqDestino é cadeia = ExtraireAvant(chaveDestino, ".") campoDestino é cadeia = ExtraireApres(chaveDestino, ".")
valor é VARIANTE = [{arqOrigem, indArquivo}..{campoOrigem}] HReadSeekFirst({arqDestino, indArquivo}, {campoDestino}, valor)
// Adiciona campos relacionados como propriedades estendidas do objeto SE HEncontrado() ENTÃO objRelacionado <- Alocar(arqDestino) objPrincipal..{arqDestino} = objRelacionado FIM FIM
_m_tabCollection.Adiciona(objPrincipal) HLitSeguinte({filePrincipal, indArquivo}) FIM FIM
// Salva no cache SE bUsaCache ENTÃO _m_cache[hashCache] = _m_tabCollection FIM
⸻
📦 Exemplo de uso real
BuildClassWithJoin("ClasseCliente", "Endereco.IDCliente=Cliente.ID;Cidade.ID=Endereco.IDCidade", Vrai)
→ Resultado: _m_tabCollection conterá objetos ClasseCliente, onde: • .Endereco está preenchido com o objeto da classe Endereco, • .Cidade está preenchido com o objeto Cidade ligado ao endereço.
⸻
🚀 Benefícios da implementação
Recurso Resultado prático JOINs dinâmicos Flexível para N arquivos Leitura performática Sem repetição de código Cache inteligente Evita reprocessamento Suporte a composição Ideal para APIs e exibição em UI
⸻
🎯 Pronto
✅ Support for dynamic JOINs across multiple tables, ✅ Optimized in-memory caching, ✅ Structured to align with your SuperClasseWX project and WX Best Practices.
⸻
✅ Purpose
This procedure dynamically builds a collection of object instances (vClass), supports JOINs between multiple related tables, and uses in-memory caching to optimize performance in repeated operations.
⸻
📐 Architecture Overview
--> BuildClassWithJoin(vClass, sJoinSpec, useCache) | |--> Retrieve the definition of the base class |--> Parse JOIN specification (sJoinSpec) |--> Build the composed query (manual JOIN or SQL) |--> If useCache = True | |--> Check memory cache (_m_cache) | |--> If cached, return collection directly |--> Otherwise: |--> Read data with HRead/Seek or HExecuteSQLQuery |--> Dynamically create instances |--> Store in _m_tabCollection and _m_cache
⸻
🔧 Procedure: BuildClassWithJoin
PROCEDURE BuildClassWithJoin(vClass, sJoinSpec = "", useCache = True)
dynObject is dynamic object classDef is Definition varDesc is Variable Description
mainFile is string mainKey is string sqlJoin is string cacheHash is string
_m_cache is a map of dynamic objects
// Allocate and get definition dynObject <- Allocate(vClass) classDef = GetDefinition(dynObject) mainFile = classDef.AttributeMapping
// Find the unique key index is int = 1 uniqueKeyFound is boolean = False
WHILE index <= classDef.Variable.Occurrence AND uniqueKeyFound = False varDesc = classDef.Variable[index] uniqueKeyFound = varDesc.AttributeUniqueKey index += 1 END
IF uniqueKeyFound = False THEN Info("No unique key found in class.") RETURN END
mainKey = varDesc.AttributeMapping
// Generate cache hash cacheHash = HashMD5(mainFile + sJoinSpec)
// Use cache if enabled IF useCache AND Exists(_m_cache[cacheHash]) THEN _m_tabCollection = _m_cache[cacheHash] RETURN END
// Manual JOIN _m_tabCollection = []
IF sJoinSpec = "" THEN // No JOIN: read main file directly HReadFirst({mainFile, fileIndex}) WHILE NOT HOut({mainFile, fileIndex}) _m_tabCollection.Add(Allocate(vClass({mainFile + "." + mainKey, fieldIndex}))) HReadNext({mainFile, fileIndex}) END
ELSE // JOIN specification: parse manually // Example: "Address.CustomerID=Customer.ID;City.ID=Address.CityID" joinArray is array of string = Split(sJoinSpec, ";")
HReadFirst({mainFile, fileIndex}) WHILE NOT HOut({mainFile, fileIndex})
// Instantiate base object objMain <- Allocate(vClass({mainFile + "." + mainKey, fieldIndex}))
// Process each JOIN FOR EACH joinClause IN joinArray
originKey is string = ExtractStringBefore(joinClause, "=") targetKey is string = ExtractStringAfter(joinClause, "=")
originFile is string = ExtractStringBefore(originKey, ".") originField is string = ExtractStringAfter(originKey, ".")
targetFile is string = ExtractStringBefore(targetKey, ".") targetField is string = ExtractStringAfter(targetKey, ".")
value is VARIANT = [{originFile, fileIndex}..{originField}] HReadSeekFirst({targetFile, fileIndex}, {targetField}, value)
IF HFound() THEN objRelated <- Allocate(targetFile) objMain..{targetFile} = objRelated END END
_m_tabCollection.Add(objMain) HReadNext({mainFile, fileIndex}) END END
// Save to cache IF useCache THEN _m_cache[cacheHash] = _m_tabCollection END
⸻
🧪 Real Use Example
BuildClassWithJoin("CustomerClass", "Address.CustomerID=Customer.ID;City.ID=Address.CityID", True)
Result: _m_tabCollection will contain objects of CustomerClass, with: • .Address: the related Address object • .City: the related City object, linked via the Address
⸻
🚀 Benefits of the Implementation
Feature Real Benefit Dynamic multi-table JOINs Scales for complex data relations Introspective object build No need for hard-coded mappings In-memory caching Blazing-fast repeated access Flexible JOIN spec Easy to plug into APIs and pagination
⸻
🎯 End
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/Mensagem modificada, julho, 13 2025 - 7:07 PM |
| |
| |
| | | |
|
| | | | |
| | |
| | |
| |
|
|
|