Página 2 de 3

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 20:15
por JoséQuintas
A do F9, pra quando digitar F9 fazer um browse do cadastro.

Código: Selecionar todos

STATIC FUNCTION Pesquisa()
   LOCAL xTela
   SAVE SCREEN TO xTela
   IF ReadVar() != "MCDCODIGO"
      RETURN NIL
   ENDIF
   dbEdit()
   IF LastKey() != K_ESC
      KEYBOARD Ltrim( Str( test->cdCodigo ) ) + Chr( 13 )
   ENDIF
   RESTORE SCREEN FROM xTela
   RETURN NIL
Nota: como só existe a variável MCDCODIGO em um único ponto do programa, só quando for esse nome é que o F9 funciona.

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 20:16
por JoséQuintas
Esta última, apesar de estar em uso em Execute(), no momento não faz nada.

Código: Selecionar todos

STATIC FUNCTION PodeAlterar()
   RETURN .T.

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 20:26
por JoséQuintas
Isso encerra a etapa de mostrar no estilo Clipper, usando variável PRIVATE e FUNCTION para tudo.
Apesar de tudo funcionando, é apenas um exemplo pra servir de base.
Cada um vai ajustar do jeito que achar melhor.

No momento, o importante é entender os diversos blocos/funções, pra entender na hora que virar uma classe.

Acho que podemos dizer que se copiarmos esse fonte pra tudo que é cadastro, e modificarmos a parte diferente, tá resolvido.

Está aí uma grande vantagem da classe: usando classe e herança, não precisamos copiar nada.
Só precisamos escrever o que for diferente.

Por enquanto o fonte como postado, no estilo Clipper/Harbour tradicional.
Quem quiser pode testar que funciona.

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 20:36
por JoséQuintas
Estou alterando durante o post, por isso tem pausa.
Por exemplo, neste momento estou declarando a classe.
É um recurso normal dos editores de texto, "encolher" o código fonte pra determinados blocos, assim pude ver os nomes (os riscos representam partes "encolhidas".
Vejam que é praticamente copiar as declarações FUNCTION alterando pra METHOD, incluindo parâmetros, se existirem.

Nota: Comentei no início sobre estas alterações.
cadastro4.png

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 20:42
por JoséQuintas
Agora indicar que pertencem à classe que chamei CadastroClass, acrescentando CLASS CadastroClass.
cadastro5.png
Agora preciso alterar cOpc para ::cOpc, e todas as chamadas pra funcionar dentro da classe, também colocando ::

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 20:53
por JoséQuintas
Basicamente no programa principal mudou isto:

Código: Selecionar todos

   #include "hbclass.ch"
   ...
   LOCAL oFrm
   oFrm := CadastroClass():New()
   oFrm:Execute()
Não precisei mais declarar cOpc como PRIVATE, pois passou a fazer parte da classe.
E tudo aquilo passou a ficar dentro da variável oFrm.

Já dentro da classe, cOpc e funções que viraram métodos tiveram que receber ::
Apenas como exemplo, a Execute()

Código: Selecionar todos

METHOD Execute() CLASS CadastroClass
   IF .NOT. ::AbreArquivos()
      RETURN NIL
   ENDIF
   DO WHILE .T.
      ::TelaDados( .F. )
      ::MenuOpcoes()
      DO CASE
      CASE LastKey() == K_ESC
         EXIT
      CASE ::cOpc == "P" // Primeiro
         ::Primeiro()
         LOOP
      CASE ::cOpc == "U" // Ultimo
         ::Ultimo()
         LOOP
      CASE ::cOpc == "-" // Anterior
         ::Anterior()
         LOOP
      CASE ::cOpc == "+" // seguinte
         ::Seguinte()
         LOOP
      ENDCASE
      IF .NOT. ::cOpc $ "IAEC"
         LOOP
      ENDIF
      IF .NOT. ::Especifico()
         LOOP
      ENDIF
      ::TelaDados()
      IF ::cOpc == "C"
         LOOP
      ENDIF
      DO CASE
      CASE ::cOpc == "I" // inclui
         IF ::PodeAlterar()
            ::TelaDados( .T. )
         ENDIF
      CASE ::cOpc == "A" // Altera
         IF ::PodeAlterar()
            ::TelaDados( .T. )
         ENDIF
      CASE ::cOpc == "E" // Exclui
         IF ::PodeAlterar()
            ::Exclui()
         ENDIF
      ENDCASE
   ENDDO
   CLOSE DATABASES
   RETURN NIL

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 21:00
por JoséQuintas
Tá... mas... e aí... tanto trabalho pra ficar igual... não mudou nada...
Fazer isso em todos os cadastros é só perda de tempo...

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 21:31
por JoséQuintas
Agora vém a parte de usar isso pra tudo que é cadastro.
Olhando as rotinas, tem várias que serão iguais pra qualquer cadastro.
Somente 3 vão ser diferentes: AbreArquivos(), TelaDados() e Especifico().

O tradicional seria... copiar tudo e alterar essas 3 rotinas.

É aqui que a brincadeira fica legal: HERANÇA..... ou..... INHERIT.

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 21:34
por JoséQuintas
O novo fonte do cadastro, o primeiro bloco...

Código: Selecionar todos

#include "hbclass.ch"

PROCEDURE Cadastro1
   LOCAL oFrm
   oFrm := PrimeiroClass():New()
   oFrm:Execute()
   RETURN
Ué... mas PrimeiroClass() não é o nome da classe?

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 22:18
por JoséQuintas
O segundo bloco, com o começo da classe:

Código: Selecionar todos

CREATE CLASS PrimeiroClass INHERIT CadastroClass
   METHOD AbreArquivos()
   METHOD TelaDados( lDigita )
   METHOD Especifico()
   END CLASS
Ué... não entendi nada....

Aqui está sendo criada a classe PrimeiroClass.
Essa classe recebe por herança (INHERIT) a classe CadastroClass.
Significa que todas as rotinas, TUDO que está na classe CadastroClass, agora também está na classe PrimeiroClass.
E não é só isso, estarei modificando estas três "rotinas".

Comparando:
No jeito tradicional, copia todo fonte, e altera o que for diferente.
Com classe, cria com herança, e altera o que for diferente.
Mesma coisa, sem copiar nada.

É só isso, e tudo isso.....

Como assim? É pouco ou é muito?

Vamos pensar nisso na prática:
Um sistema, principalmente de nota fiscal, tem dezenas de cadastros.
Vixi... De cara já é vantagem.... muito código fonte pode ser eliminado.
Converter fontes antigos então... economiza muito tempo, porque o fonte da sua classe estará testado e pronto pra uso.

E o que mais....

Quer alterar o menu de opções de todos os cadastros de uma vez?
Ele está na classe principal, só alterar a classe principal e pronto.

Quer alterar o menu de opções pra gráfico?
Só alterar a classe principal e pronto.

Quer alterar o menu pra parte de cima, ao invés da parte de baixo?
Aí temos que pensar...
Foi o que eu fiz...
Alterei TelaDados() e Especifico() pra usar Row() + 1 ( linha atual da tela + 1 )
Alterei a classe pra quando entrar nessas opções posicionar no lugar correto.
E por fim, alterei o menu da classe principal, pra botões gráficose parte de cima.
A única coisa que o menu precisa fazer é retornar a letra da opção, não importa como o menu faz isso.

E por aí vai.

Eu comecei a criar a classe pra cadastros, conforme fui fazendo fui vendo o que precisava a mais e fui ajustando.
Nos pedidos, por exemplo, vi necessidade de algo mais, porque nem sempre pode alterar um pedido.
No início do post mostrei uma função reserva: PodeAlterar()

Serve como um exemplo simples de uso, onde a herança nos pedidos cria uma PodeAlterar() própria:

Código: Selecionar todos

METHOD PodeAlterar() CLASS PedidoClass()
   IF pedido->Status == "N"
      Mensagem( "Não pode mexer em pedido com nota emitida" )
      RETURN .F.
   ENDIF
   IF UserLevel() < 1
      Mensagem( "Usuário não tem permissão de alterar pedido" )
      RETURN .F.
   ENDIF
   RETURN .T.
A mesma rotina simples de cadastro, usada pra digitação de pedidos, com checagem de nível de usuário e status de pedido.
Num caso desses, não vai deixar alterar ou excluir.

No caso dessa PodeAlterar(), o método original não fazia nada, mas a classe ficou pronta pra receber esse "adicional".

Basicamente é isso que uso, mas a minha classe tem muito mais coisas, que fui ajustando conforme fui usando para todo o sistema.
Por exemplo, nos pedidos, somente o menu básico não basta, acrescento mais opções no menu.

E acabei usando a classe básica até mesmo para um preview de impressão, pra navegar pelas páginas...

O limite é a imaginação.

E o negócio é não ter pressa.
Vai fazendo devagar, projetando com calma, e alterando um fonte de cada vez, sempre com calma.
Lembre-se que se mudar de idéia, por exemplo alterar o nome de TelaDados() pra outra coisa, terá que alterar todos os fontes que já tiver mexido,
Alterando um cadastro de cada vez vai confirmando se a classe atende ou se precisa de ajuste.

A que postei funciona, mas com certeza precisa ajuste na parte de inclusão, por exemplo, que já cadastra um registro com o código digitado.

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 22:24
por JoséQuintas
Pra ficar no post, a classe desse cadastro completa, que mostra como ficariam seus cadastros "normais":

Código: Selecionar todos

#include "hbclass.ch"

PROCEDURE Cadastro1
   LOCAL oFrm
   oFrm := PrimeiroClass():New()
   oFrm:Execute()
   RETURN

CREATE CLASS PrimeiroClass INHERIT CadastroClass
   METHOD AbreArquivos()
   METHOD TelaDados( lDigita )
   METHOD Especifico()
   END CLASS


METHOD AbreArquivos() CLASS PrimeiroClass
   LOCAL oStru
   IF .NOT. File( "test.dbf" )
      oStru := { { "CDCODIGO", "N", 6, 0 }, { "CDNOME", "C", 30, 0 } }
      dbCreate( "test", oStru )
   ENDIF
   IF .NOT. File( "test.cdx" )
      USE Test
      INDEX ON test->cdCodigo TAG "CODIGO"
      USE
   ENDIF
   USE test
   SET INDEX TO test
   RETURN .T.

METHOD TelaDados( lDigita ) CLASS PrimeiroClass
   LOCAL GetList := {}
   LOCAL mcdCodigo := test->cdCodigo
   LOCAL mcdNome   := test->cdNome
   lDigita := iif( lDigita == NIL, .F., lDigita )
   @ 2, 0 SAY "Codigo....:" GET mcdCodigo PICTURE "@K 999999" WHEN .F.
   @ 4, 0 SAY "Nome......:" GET mcdNome   PICTURE "@!"
   IF lDigita
      Mensagem( "Digite informações, <ESC> Sai" )
      READ
      Mensagem()
      IF LastKey() != K_ESC
         REPLACE test->cdNome WITH mcdNome
      ENDIF
   ELSE
      CLEAR GETS
   ENDIF
   RETURN NIL


METHOD Especifico() CLASS PrimeiroClass
   LOCAL GetList := {}
   LOCAL mcdCodigo := test->cdCodigo
   Mensagem( "Digite código, <ESC> Sai" )
   @ 2, 0 SAY "Codigo....:" GET mcdCodigo PICTURE "@K 999999" VALID mcdCodigo > 0
   READ
   Mensagem()
   IF LastKey() == K_ESC
      RETURN .F.
   ENDIF
   SEEK mcdCodigo
   IF ::CodigoInvalido()
      RETURN .F.
   ENDIF
   IF ::cOpc == "I"
      APPEND BLANK
      REPLACE test->cdCodigo WITH mcdCodigo
   ENDIF
   RETURN .T.

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 22:50
por JoséQuintas
Esqueci... tem o lado ruim.

Infelizmente, não tem checagem de variáveis e métodos de classe.

Se usar uma função que não existe, Minhoca(), o compilador dá erro que falta a função.

Mas se usar método de uma classe que não existe, o erro vai ser em tempo de execução.
oFrm:Minhoca()

A mensagem de erro vai ser algo parecido com: MESSAGE NOT FOUND
E pode acusar o erro na classe do cadastro: TestClass() por exemplo, no método Primeiro().
É bom lembrar que esse Primeiro() é da classe principal, e não da TestClass() - recebeu por herança.

É questão de ir acostumando com a novidade.

Aqui tinha feito um único fonte pra tudo.
Estou separando a classe, do cadastro, e do programa principal, pra enxergar melhor o que pertence a cada um.
Depois posto os fontes finais, incluindo um segundo cadastro ou terceiro... vamos ver até onde dá...

Cadastro poderoso usando classe

Enviado: 24 Mai 2015 23:11
por JoséQuintas
Deixei com dois cadastros.
Pra não fazer menu, depois de sair de um, entra no outro.
Acompanha fontes, arquivo hbp, DBFs com alguns cadastros de teste, e EXE compilado com Harbour 3.4 VSzakats

Rotina pesquisa ajustada pra funcionar com os dois arquivos, usando o campo chave de referência.

Estrutura totalmente diferente dos dois arquivos.

Test.prg - programa principal
CadastroClass.prg - Classe principal
Cadastro1.prg - Primeiro cadastro
cadastro2.prg - Segundo cadastro
test.dbf - primeiro arquivo
outro.dbf - segundo arquivo

Nota: não é a primeira vez que posto esse tipo de classe, mas desta vez foi com explicação detalhada, e com o equivalente sem usar classe.
Nota2: O fonte sem usar classe está nos primeiros posts

Cadastro poderoso usando classe

Enviado: 25 Mai 2015 00:16
por fladimir
Obrigado por compartilhar...

Já trabalhava com Classe mas nunca dominei, com tua explanação, clareou mais alguns pontos, obrigado, um dia fico bom nisso.

[]´s

Cadastro poderoso usando classe

Enviado: 25 Mai 2015 08:02
por Toledo
José Quintas, Fantástico... Parabéns, muito bem detalhado!

Obrigado por compartilhar!

Abraços,