Arquivo de tag totvs

porPaulo Henrique Corrêa Cardoso

Escopo de variáveis ADVPL

Fala pessoal, tudo bem ? Hoje tenho um assunto bem bacana pra quem trabalha com o desenvolvimento de sistemas Protheus® na linguagem ADVPL. Vamos entender melhor como funciona o escopo de variáveis e qual é o melhor para se utilizar em cada caso, isso impacta diretamente em performance e memória quando se trata de sistemas de grande escala. Primeiramente vou listar quais são os tipos de escopo e exemplificar cada um deles.

Global

Variáveis globais conseguem carregar suas informação para qualquer thread criada após a sua definição. São muito úteis quando precisamos definir ou recuperar valores em rotinas de processamento multi-thread. Devemos tomar cuidado ao utilizá-la, pois a mesma ficará disponível para qualquer execução iniciada após sua definição, podendo acarretar problemas de sobreposição caso concorra com mais de uma execução. No exemplo a seguir vamos definir duas variáveis globais que terão seus valores modificados por uma função executada em outra Thread, e na sequência recuperar seus valores na função atual.
User Function ExJobXPT()
  Local lRetJob := .F.

  // Tenta obter acesso exclusivo pra gravar a variável global
  While !( GlbLock() )
    Sleep(5)
  End While
  
  //Define e seta o valor nas variáveis globais
  PutGlbValue("lRetExec", lRetJob )
  PutGlbValue("cTexto", "Teste" )
  
  //Libera o acesso excluvivo a pilha de variáveis globais
  GlbUnlock()
  
  //Executa rotina em uma nova Thread 
  StartJob("U_ExecXPTO",GetEnvServer(),.T.)
  
  // Le os valores gravado pela função onde a váriavel global foi alterada.
  lRetJob  := GetGlbValue("lRetExec")
  If lRetJob
    Conount(GetGlbValue("cTexto"))
  Else
    Conount("Retornou Falso")
  EndIf
Return

Public

A variável Public e uma variável que fica valida em todo o escopo de execução do sistema ou seja do momento em que a mesma for criada até o momento que fizer a desconexão da sessão ativa ou destruição da mesma. É uma variável que temos que tomar cuidado ao utilizar, justamente pelo escopo abrangente que a mesma possui. A criação sem necessidade podo causar problemas de lentidão ou até mesmo problemas em tempo de execução do programa caso o mesmo não seja preparado para isso. A mesma pode ser utilizada para definir informações que serão utilizadas em diversas rotinas do sistema. Elas são equiparadas as Session’s de  outras linguagens. Alguns exemplos de variáveis públicas existentes são : cFilAnt, cEmpAnt, dDatabase, cModulo. No exemplo a seguir vamos ver um caso onde guardamos a hora inicial da chamada de uma função de menu, e verificamos se o tempo que demorou para entrar em outra função foi maior que 3 minutos.
// Função executada em um botão do menu do protheus
User Function ProcInic()
    Public cHoraIni := TIME()
    
    // Seu Código
Return


// Função executada em um outro botão do menu do protheus
User Function ProcFim()
    Local cDifTime := ElapTime ( cHoraIni, TIME() )
    If cDifTime > "00:03:00" 
        Alert("O tempo de execução entre a função inicial e final demorou mais de 3 minutos.")
    EndIf

Return

Static

As variáveis do tipo Static tem seu escopo definido dentro da execução de uma thread, ou seja, consegue ser acessado por qualquer função hierarquicamente acima ou abaixo do local onde foi declarada, normalmente as variáveis Static são declaradas logo após a definição das includes do fonte e podem ser utilizadas somente pelo programa fonte corrente. No exemplo abaixo, vamos incrementar a variável em cada nível de função chamada.
#Include "Protheus.ch"

Static nVar := 0

User Function Pai()
  nVar += 10
  conOut("Pai")
  conOut(nVar)
  Filho()
  
  // Muito importante limpar o valor da váriavel static ao concluir sua utilização
  nVar := 0
Return
 
Static Function Filho()
  nVar += 10
  conOut("Filho")
  conOut(nVar)
  Neto()
Return
 
Static Function Neto()
  nVar += 10
  conOut("Neto")
  conOut(nVar)
Return
Muito importante lembrar a necessidade de limpar a variável ao final de sua utilização, pois se as funções foram executadas novamente sem essa limpeza pode acarretar em valores errôneos.

Private

O escopo da variável Private tem sua definição valida para todas as funções hierarquicamente chamadas após a sua declaração, independente do programa fonte utilizado e a mesma se encerra ao final da execução da sua função de origem. Este tipo de escopo não necessita do identificador Private para ser criada, basta escrever o nome da variável não existente que em momento de execução o ADVPL cria como Private, isso pode ser um grande problema em caso de uma exceção, pois será muito difícil  encontrar onde a mesma foi criada. Vejo muitos analistas/programadores que não declaram as variáveis, isso além de deixar o código muito confuso pode causar problemas em tempo de execução e aumento de memória. No exemplo abaixo podemos ver que a variável cNome definida como Private dentro da função Filho() consegue ser acessada pela função Neto(), porém não consegue ser acessada pela função U_Pai().
User Function Pai()
  Filho() 
  If Type("cNome") == "C"
    conOut(cNome)
  Else
    conOut("Variável não definida")
  EndIf
Return
 
Static Function Filho()
  Private cNome := "Paulo"
  Neto()
  conOut("Filho")
  conOut(cNome)
Return
 
Static Function Neto()
  conOut("Neto")
  If Type("cNome") == "C"
    conOut(cNome)
  EndIf
Return

Local 

O escopo Local é o escopo de menor abrangência, ou seja, ele só funciona dentro da função onde a variável foi declarada, e é o escopo que devemos utilizar com mais frequência, pois terá o menor tempo de vida útil dentro da execução, consequentemente o menor custo de memória. No exemplo abaixo precisamos definir a variável local dentro da função onde queremos utilizar, note que a função Filho() não encontrará a variável cVarNome se a mesma não for declarada.
User Function Pai()
  Local cVarNome := "Paulo"
  conOut(cVarNome)
  Filho()
Return
Static Function Filho() 
  Local cVarNome := "Henrique"
  conOut(cVarNome)
Return
  
  Um detalhe muito importante do escopo de variáveis é que elas podem ser subsistidas por uma variável de escopo menor caso a mesma seja declarada dentro do local utilizado, por exemplo. Se eu possuir uma variável Public cXPTO com o valor “A” já declarado e dentro de uma novo fonte eu declarar uma variável Static cXPTO  com o valor “B” as duas variáveis existirão, mas o sistema vai priorizar o escopo mais baixo para a utilização no processamento corrente. Esta sequência hierárquica de escopo é : PublicStatic  > Private > Local .  O escopo Global não entra nessa hierarquia pois o mesmo é definido e acessado de forma diferente dos outros.   É isso pessoal, espero que ajude muito vocês e se tiveram alguma dúvida só deixar nos comentários.  

Até a próxima !!!