PC SOFT

GRUPOS DE DISCUSSÃO PROFISSIONAL
WINDEVWEBDEV 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