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 😉
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.
CurtirCurtir
Olá Enderson,
Existe uma função chamada ClassDataArr(), que a partir da instância de uma classe, retorna todas as suas propriedades ( http://tdn.totvs.com/display/tec/ClassDataArr ) , Vou dar uma olhada se existe função similar para métodos 😉
Abraços
CurtirCurtido por 1 pessoa
Pela dica que vc deu, acabei encontrando um post no BlackTDN com referência a um método ClassMethArr(), que recupera os métodos, vou fazer uns testes e qq coisa compartilho por aqui os resultados.
Fonte: http://www.blacktdn.com.br/2011/11/protheus-advpl-resolvendo-limitacao-da.html
CurtirCurtir
Basicamente estou tentando portar um projeto antigo feito para xHarbour, chamado xhbunit.
Estou fazendo ele compilar OK em Harbour, sem nenhuma dependência de libs externas, e também torná-lo compatível com o ADVPL.
Estou empacado em dois métodos, o __ObjGetMethodList( OBJECT ) que pretendo substituir pelo ClassMethArr().
Estou agora pesquisando sobre o e o __ObjSendMsg( OBJECT, METHOD ) que imegino que dê para substituir com a macro &.
* Chamada para o __ObjGetMethodList – https://github.com/endersonmaia/hbunit/blob/totvs/lib/TestResult.class.prg#L73
* Chamada para o _ObjSendMsg – https://github.com/endersonmaia/hbunit/blob/totvs/lib/TestResult.class.prg#L90
Fique a vontade para colaborar com projeto 😉
CurtirCurtido por 1 pessoa
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 ?
CurtirCurtido 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 😉
CurtirCurtido 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 ?
CurtirCurtido 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. 😉
CurtirCurtido 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 ?
CurtirCurtir
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.
😉
CurtirCurtido 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. 😉
CurtirCurtido por 1 pessoa
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 ?
CurtirCurtido por 1 pessoa
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
CurtirCurtido por 1 pessoa
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 ;
CurtirCurtido 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 !!!
CurtirCurtir
Blz Julio, Obrigado pelo esclarecimento .. Abraços
CurtirCurtir
Pingback: Orientação a Objetos em AdvPL – Parte 01 | Tudo em AdvPL