CodeBlocks em AdvPL – Parte 02

Introdução

No post anterior, CodeBlocks em Advpl – Parte 01, vimos como criar, parametrizar, chamar e obter retorno de um Codeblock. Agora, vamos ver como criar Codeblocks dinamicamente, e explicar o que é e como funciona as referências de variáveis que o Codeblock faz.

Codeblock dinâmico

Em determinadas situações, pode ser necessário criar um Codeblock com conteúdo dinâmico. Neste caso, usamos o operador de macro-execução (&) do AdvPL, por exemplo:

cFnCall := "u_minhafunc"
cBloco := " {|x| "+cFnCall+"(x) }"
bBloco := &(cBloco)

/* ----------------------------------------------------
cFnCall é uma variável caractere que tem o nome de uma 
        função a ser chamada. 
cBloco é uma variável caractere que você monta com o 
       corpo do Codeblock, montando a chamada da função
bBloco será o Bloco de Código, criado dinamicamente pelo
       operador de macro-execução.
---------------------------------------------------- */

Observação

Uma vez que eu crie um bloco de código dinamicamente, usando macro-execução, caso dentro do texto do Codeblock eu faça uma referência a uma variável local, ela não será visível dentro do Codeblock, pois o operador de macro-execução não acessa o escopo local.

Referência a variáveis do escopo local

O Codeblock não apenas é uma forma de criar uma função anônima encapsulada dentro de uma variável, que pode ser passada para outras funções como parâmetro ou pode ser retornado para funções da pilha de chamadas anterior. Quando ele é criado dentro de um determinado fonte, o corpo do Clodeblock pode fazer referência e usar variáveis do escopo local da função onde ele foi declarado — desde que as variáveis usadas no corpo do Codeblock já tenham sido declaradas. Vamos a um exemplo que fica mais fácil de compreender.

User Function Bloco()
Local bBloco
bBloco := GetBloco()
eval(bBloco,10)
MsgInfo("Total",eval(bBloco))
Return

Static Function GetBloco()
Local nTotal := 0 
Local bBloco := { |x| IIF( PCount() == 0 , nTotal , nTotal := x ) }
Return bBloco

Reparem que a variável nTotal é local, declarada dentro da função GetBloco(), e dentro da função, ela é usada dentro do Codeblock. O bloco de código montado é um Pattern do tipo GET-SET: Se for passado um parâmetro, a variável nTotal recebe um conteúdo, se nenhum parâmetro for passado, o bloco retorna a variável nTotal.

Porém, a variável nTotal é local dentro do escopo da função GetBloco(). Porém, como ela foi usada dentro do corpo do Codeblock, esta variável não vai ser descartada. Mesmo eu retornando o Codeblock para o nível anterior, o Codeblock continua funcionando. Na primeira chamada com Eval(), passando o valor 10, este valor é armazenado na variável nTotal. Na segunda chamada do Codeblock, sem passar parâmetros, o valor é retornado.

Justamente por fazer referências as variáveis de onde o Codeblock é criado, não é recomendável criar um Codeblock dentro de uma função e retorná-lo ao nível anterior da pilha de chamadas, pois ao retornar o Codeblock, o Runtime do AdvPL não pode limpar as variáveis e seus respectivos conteúdos daquela execução, pois o Codeblock retornado ao nível anterior arrasta com ele este contexto. Somente haverá a limpeza deste contexto quando o bloco de código não for mais referenciado — isto é, todas as variáveis que referenciam este Codeblock devem ser anuladas.

Da mesma forma, um Codeblock montado no nível atual, e passado para frente como parâmetro para outra função, ao referenciar uma variável local de um escopo, a chamada do bloco vai agir sobre esta variável. Vamos ao segundo exemplo:

User Function Bloco()
Local nTotal := 0 
Local bBloco := { |x| IIF( PCount() == 0 , nTotal , nTotal := x ) }
SetTotal(bBloco,10)
MSgInfo(nTotal,"Total")
Return

Static Function SetTotal(bBloco,nValor)
eval(bBloco,nValor)
Return

O bloco continua referenciando a variável nTotal, criada na função U_BLOCO(). Porém, o bloco foi passado como parâmetro para a função SetTotal(), e de dentro dela chamamos o Eval(), repassando o valor 10. O bloco vai fazer a atribuição na variável nTotal, do escopo anterior, pois ela foi referenciada no bloco.

A classe tGet ou o comando @ … GET do AdvPL recebe e/ou monta um Codeblock do tipo GET-SET amarrado na variável local. Isso permite que, ao declarar a variável a ser usada, o componente de interface faz referência a esta variável, de modo que esta variável pode ser passada como parâmetro por exemplo para um determinado botão. Uma vez que você atualize na interface o conteúdo daquele objeto tGet, a variável de memória usada é atualizada, sempre pegando o conteúdo atualizado quando você chama uma função usando a variável daquele escopo.

Recomendações

Existem componentes, principalmente de interface, que somente usam Codeblocks. Não têm problema nenhum em usá-los, apenas tome cuidado para não criar monstros. Se você tem uma sequencia de operações muito grande para chamar dentro de um Codeblock, o código fica mais elegante e legível se você fazer uma função — uma STATIC Function por exemplo — e chamá-la de dentro do Codeblock. Lembre-se dos princípios de simplicidade e legibilidade de código.

Conclusão

Acredito que este post contempla o que faltava mencionar sobre Codeblocks. O que vai fazer a diferença na sua aplicação é como você usa us recursos disponíveis da sua plataforma tecnológica.

Agradeço a todos pela audiência, e lhes desejo TERABYTES DE SUCESSO 😀

“Qualquer um pode escrever um código que o computador entenda. Bons programadores escrevem códigos que os humanos entendam.” — Martin Fowler

“Um excelente programador escreve códigos simples que realizam tarefas complexas.” — Júlio Wittwer

 

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s