Classes em Advpl – Parte 03

No tópico anterior, vimos um exemplo de uma classe ADVPL herdando outra classe em ADVPL. Vimos também que a herança não pode ser múltipla, isto é, uma classe não pode herdar mais de uma classe pai ao mesmo tempo, e vimos também que é possível herdar uma classe que já possua herança. Agora, vamos criar uma classe ADVPL que herda uma classe básica da linguagem. Vamos criar uma classe de botão diferenciada, herdando a classe básica de botão do ADVPL (tButton).

Desta vez sem muita teoria, os dois tópicos anteriores já cuidaram dessa parte ! Agora, as explicações ficam pro final do tópico, vamos ao código: Crie um fonte novo (extensão .PRW), copie, cole, salve, compile e execute U_APTST03 através do Smartclient.

// --------------------------------------------------
// Fonte de teste da classe APBUTTON herdando tButton
User Function APPTST03() 
Local oDlg , oBtn1, oBtn2
DEFINE DIALOG oDlg TITLE "Exemplo de Herança" FROM 10,10 TO 150,300 COLOR CLR_BLACK,CLR_WHITE PIXEL
// Cria um botao normal
// e seta cor diferenciada para o botão 
@ 10,5 BUTTON oBtn1 PROMPT 'TBUTTON' ;
 ACTION ( oBtn2:Show() , oBtn1:Hide() ) ;
 SIZE 040, 013 OF oDlg PIXEL
// Cria um botao usando a classe implementada
oBtn2 := APBUTTON():NEW(oDlg, "APBUTTON", 30, 5, 40, 13, {|| oBtn1:Show(),oBtn2:Hide() })
ACTIVATE DIALOG oDlg CENTER
Return
// ------------------------------------------------------------
CLASS APBUTTON FROM TBUTTON
METHOD New() CONSTRUCTOR
 METHOD Hide()
 METHOD Show()
 
ENDCLASS
// Construtor da classe inicializa construtor do botão 
// e já seta todas as propriedades e comportamentos desejados
// ( Troca fonte, seta cor e esconde o botão ) 
METHOD New(oParent,cCaption,nTop,nLeft,nWidth,nHeight,bAction) CLASS APBUTTON
:New(nTop,nLeft,cCaption,oParent,bAction,nWidth,nHeight,NIL,NIL,NIL,.T.)
::SetColor(CLR_WHITE,CLR_BLACK)
::SetFont( TFont():New("Courier New",,14))
_Super:Hide()
Return self
METHOD Hide() CLASS APBUTTON
MsgInfo("Escondendo o botão ["+::cCaption+"]")
Return _Super:Hide()
METHOD Show() CLASS APBUTTON
MsgInfo("Mostrando o botão ["+::cCaption+"]")
Return _Super:Show()

As diferenças

Os dois botões são criados de formas diferentes, o botão oBtn1 usando a definição padrão do ADVPL, e o oBtn2 usando a nossa classe APBUTTON, que herda TBUTTON. A primeira diferença é o construtor. A herança de classe básica do ADVPL exige que a primeira linha do método construtor chame o construtor da classe pai da herança, usando apenas “:”+nomedoconstrutor+”(“+parâmetros+”)” . A utilização da diretiva “_Super:” dentro da implementação dos métodos funciona da mesma forma, exceto para a chamada do construtor, que exige a grafia diferenciada.

Ao executar o programa acima, devemos ver um botão do ADVPL, que ao ser clicado mostra o novo botão APBUTTON e esconde o botão pressionado. Antes de mostrar o novo botão, como o método Hide() foi reimplementado, será mostrada uma mensagem informativa. A mesma coisa acontece para o método Show(), porém apenas do botão implementado com a classe APBUTTON. A ação do botão APBUTTON será mostrar novamente o botão ADVPL e esconder-se.

Os limites

Bem, até aqui tudo é lindo, mas estes recursos possuem alguns limites específicos. Atualmente, uma classe ADVPL que herda uma classe do binário não pode ser herdada por outra classe ADVPL. Caso você tente por exemplo criar uma classe APBUTTON2 que herda APBUTTON, a mesma vai compilar, mas na hora de executar será gerado um erro de inicialização do construtor nos níveis superiores. Já a herança de classe ADVPL atualmente suporta apenas 2 níveis de herança. Por exemplo, classe FILHA FROM PAI, NETA FROM FILHA. Se você implementar a classe BISNETA e tentar herdar a classe NETA, ao executar por exemplo o construtor da BISNETA, onde haverá uma cascata de _Super para os construtores das camadas superiores, ( BISNETA -> NETA -> FILHA -> PAI ), a execução dos construtores entra em loop, finalizando o processo em execução com uma ocorrência de erro Advpl “stack depth overflow”.

As boas práticas

Inicialmente, além de procurar respeitar os limites estabelecidos, existem características de retenção de memória ligados ao uso de classes visuais e não-visuais no ADVPLe boas práticas gerais ligadas à orientação a objetos em geral, que devido ao nível de detalhamento e quantidade de tópicos, estes temas serão tratados nos próximos posts.

Conclusão

Estes tópicos servem como uma base, uma introdução ao assunto com alguns detalhes. O que vai fazer a diferença na utilização destes recursos é você pesquisar mais sobre o tema, e começar a usá-los no seu dia a dia, a experiência adquirida com alguns calos nos dedos e neurônios queimados usando estes recursos é que vai fazer a diferença. Na TDN, existe um guia completo das classes de interface visual e não-visual da linguagem Advpl, no link “http://tdn.totvs.com/pages/viewpage.action?pageId=6063177“. Para absorver este conteúdo, ler não é o bastante … é apenas o princípio !

Até o próximo post, pessoal 😉

17 respostas em “Classes em Advpl – Parte 03

  1. Eu estou experimentando OO com o ADVPL e para testar localmente, tenho usado o Harbour.

    Estive testando alguns métodos como __ObjGetMethods() para recuperar a lista de métodos de uma classe, no Harbour. Gostaria de saber se existe algum lugar onde possa conseguir informações sobre métodos equivalentes no ADVPL.

    Curtir

  2. Olha eu novamente!

    No Harbour existe o CLASSDATA, para definir variáveis de Classe, diferente do DATA que define variáveis da instância da classe ( do objeto.).

    Existe algo equivalente ao CLASSDATA no ADVPL ?

    Curtido por 1 pessoa

    • Olá Enderson,

      Algo como o CLASSDATA não tem no AdvPL, porém você teria como emular isso criando um método para set e outro para get do valor, encapsulando uma variável STATIC no fonte da classe.

      Abraços 😉

      Curtido por 1 pessoa

      • Gostei da ideia, mas andei pesquisando nos includes,e vi que tem um tal de msobject.ch, sabes dizer se ele já é incluído quando eu faço include do totvs.ch ?

        Se vc reparar no código dele, tem as definições para CLASSDATA.

        https://github.com/imsys/Protheus-Include/blob/bd10d5a27bec926b74e4480d4349590abe1839c2/include/msobject.ch#L72-L75

        Andei testando aqui, mas sem sucesso. Será que esse include está sendo usado mesmo ?

        Curtido por 1 pessoa

      • Sim, realmente este include é utilizado, porém DATA” e “CLASSDATA” são tratados da mesma forma, no Advpl não existe (ainda) o conceito de CLASSDATA como no Harbour. 😉

        Curtido por 1 pessoa

      • Me tira uma dúvida. Mesmo usando STATIC no fonte da classe, o escopo desta variável seria global, certo ?

        Ou quando vc fala no fonte da classe, é especificamente dentro do escopo CLASS … ENDCLASS ?

        Curtir

      • No AdvPL, o escopo da declaração de uma variável STATIC é sempre o arquivo-fonte onde a variável foi declarada, inclusive por isso eu sempre declarei variaveis STATIC fora do corpo de funções ou metodos. E, qualquer função declarada dentro deste mesmo fonte (.prw) acessa o conteudo daquela variavel, que será unico por processo / thread em execução independente do stack.

        😉

        Curtido por 1 pessoa

      • E, apenas pra confirmar o comportamento, fiz um teste declarando as variaveis STATIC dentro do corpo de funções, classes ou métodos, o comportamento foi exatamente o mesmo: Como o escopo da STATIC é restrito ao código fonte onde ela foi declarada, ela pode estar em qualquer lugar do fonte. Porém, é elegante que todas as variáveis STATIC de um fonte AdvPL sejam declaradas no início do fonte. 😉

        Curtido por 1 pessoa

  3. Olá Siga0984,

    ainda não encontrei uma solução para invocar um método de um objeto.

    No Harbour eu uso o __ObjSendMsg( OBJECT, METHOD ), tentei fazer com EVAL, mas não rolou.

    Você poderia me dar alguma luz nisto ?

    Curtido por 1 pessoa

  4. Vejamos … a alternativa mais interessante é usar a macro-execução do AdvPL junto do Eval(). Veja exemplo abaixo, onde eu crio um CodeBlock dinâmico para fazer a chamada por Eval():

    Function myObjSndMsg( oObj , cMethod )
    Local xRet , bCall
    bCall := &(“{ |o| o:”+cMethod+”() }”)
    xRet := Eval( bCall , oObj)
    Return xRet

    Porem, se você for precisa passar parâmetros, o mais elegante a fazer é:

    Function myObjSndParm( oObj , cMethod , _p1,_p2, _p3, _p4)
    Local xRet , bCall
    bCall := &(“{ |o,p1,p2,p3,p4| o:”+cMethod+”(p1,p2,p3,p4) }”)
    xRet := Eval( bCall , oObj, _p1, _p2, _p3, _p4)
    Return xRet

    Acho que isso resolve 😀

    Abraços

    Júlio Wittwer
    siga0984@gmail.com
    https://siga0984.wordpress.com/
    https://www.facebook.com/siga0984

    Curtido por 1 pessoa

  5. Bom dia Julio,

    Primeiramente queria te agradecer por compartilhar o seu conhecimento através deste blog, começei a utilizar OO em ADVPL e ao Herdar a classe FWMBrowse() tive o problema que você mencionou acima “stack depth overflow” hehe … mas o que eu gostaria de saber mesmo é sobre classe e métodos abstratos, eles podem ser implementados em ADVPL ? eu peguei uns fontes que o pessoas faz assim:

    method lOpen() class XXXXXXXXX
    abstract
    return

    não entendi o por que, mas ao tentar fazer o mesmo não consegui compilar, tentei utilizar antes method abstract nomeDoMeuMetodo() e tbm não compilou ..

    vlw Julio e mais uma vez parabens pelo seu blog ;

    Curtido por 1 pessoa

    • Opa, beleza Evandro ?

      Então, agradeço a audiência e o apreço ..rs… Quanto a orientação a Objetos em AdvPl, ela originalmente é muito fraca na questão de escopo. Todos os métodos e propriedades são públicas.

      Existem algumas diretivas implementadas em includes, que na verdade não são interpretadas.

      O AdvPl provavelmente vai caminhar para uma evolução na orientação a Objetos, como a implementação de tipagem e escopo de propriedades e métodos, mas estes recursos ainda estão “no forno” 😀

      Abraços e saudações !!!

      Curtir

  6. Pingback: Orientação a Objetos em AdvPL – Parte 01 | Tudo em AdvPL

Deixe um comentário