classes, relatórios e comentários intruções e padrões iniciais · obviamente, abstração é...
TRANSCRIPT
Manual do Desenvolvedor
Classes, Relatórios e Comentários –
Intruções e Padrões Iniciais
Esse documento é uma obra intelectual de uso restrito da Atual Sistemas. Qualquer ato de leitura, alteração, cópia ou distribuição,
completa ou parcial deve ser feita somente sob autorização expressa. A posse ou uso não autorizado desse documento, ou do seu
conteúdo completo ou parcial, constitui-se um uma violação direta dos direitos de intelectualidade e será julgada conforme o rigor da lei.
Fascículo II
Julho/2011
ii Fascículos do Desenvolvedor
Conteúdos
Criação e Uso de Classes 1
Abordagem Geral sobre relatórios e seu Desempenho 9
Método Padrão para Comentários 17
Capítulo 1
Criação e Uso de Classes
A linguagem DataFlex ao longo dos tempos tem passado por algumas modificações, e podemos afirmar que o modelo de
desenvolvimento orientado a objetos foi uma das mudanças mais significativas da linguagem. Embora o paradigma de
Programção Orientada a Objetos (conhecido pela sigla internacional de OOP) não seja exclusivo a alguma linguagem em si,
o conceito tem variações nos seus aspectos nas várias linguagens em que é incorporado.
O conteúdo desse capítulo não representa o conceito padrão de OOP, mas sim o que o VDF em si suporta e entende por
OOP. Isso deve ser levado em conta, já que nem todos os aspectos de OOP presentes na maioria das linguagens fazem parte
do VDF, enquanto princípios elementares de OOP que se comportam de maneira básica em quase todas as linguagens
possuem leves diferenças em VDF. Assim, todas as afirmação sobre OOP nesse capítulo não devem ser julgadas em um
contexto geral.
Classes na prática e o VDF
Programas não-orientados a objetos são compostos de uma longa lista de comandos. À medida que esses programas ficam
mais complexos, são formados grupos de funções e sub-rotinas para executar variadas tarefas. Com um design como esse
não raro funções e dados podem ser alcançados a partir de qualquer parte do programa. Conforme o programa cresce,
permitir que funções alterem dados indiscriminadamente a partir de qualquer lugar do programa pode levar a bugs com
efeitos de longo alcance.
Nota Nesse capítulo, quando nós nos referimos a dados, estamos nos referindo a qualquer informação
em memória e não a dados dentro de um banco de dados.
Por outro lado, desenvolvimento por OOP incentiva o programador a colocar os dados em um ponto que eles não são
diretamente acessíveis pelo resto do programa. Assim, os dados são acessados por funções escritas especialmente para isso,
chamadas de métodos, que são agrupadas ou herdadas de uma “classe de objetos” e agem como intermediárias para acesso
e alteração desses dados. Uma construção que combina dados com um grupo de métodos para gerenciar esses dados é
chamada de Objeto.
Saber que objetos são como construções indica que podemos ter o número de objetos que desejarmos dentro de um
programa, conforme a necessidade. Também podemos ter vários objetos cuja construção é idêntica para ambos, embora
seus dados possam sofrer alterações de valores e diferir entre si ao longo da aplicação, da mesma forma que eu posso ter
três martelos da mesma marca e do mesmo modelo, sendo um mais velho que os outros dois e, portanto tendo diferenças
dos outros dois.
A planta, ou projeto, dessas construções, responsável em determinar as características impares de um objeto é chamada
Classe. Classes servem como modelos na criação de objetos (também chamados de instâncias). Nelas nós especificamos a
composição do objeto que estamos criando o que inclui os métodos que serão usados, os dados que estarão presentes, se
esse objeto é composto por outros objetos, se ele deve ser feito a partir das definições de outra classe e se a definição desse
objeto deve ser combinada com a definição de outra classe.
Em VDF, para criar classes de objetos para construção desse tipo, nós usamos Procedure e Function na definição dos
métodos. Para os dados, nós usamos Property. O código a seguir demonstra como criar e instanciar uma classe.
Class cTarefa is a cObject
Procedure Construct_Object
Forward Send Construct_Object
Property Integer piDados
End_Procedure
Procedure Dados Integer iDados
2 Fascículos do Desenvolvedor
Set piDados to iDados
End_Procedure
Function Get Dados Retuns Intenger
Funtion_Return (piDados(Self))
End_Function
End_Class
// ...
Object oTarefa1 is a cTarefa
Set piDados to 10
End_Object
Object oTarefa2 is a cTarefa
Set piDados to 15
End_Object
Conforme pode ser notado, foi feita a planta da classe cTarefa onde se especificou quais métodos e dados são necessários
para essa construção. Isso foi feito dentro do bloco Class... End_Class. Toda estrutura informada dentro desse bloco serve
simplesmente como um modelo, e nenhum desses métodos ou dados serão carregados na memória simplesmente por se
ter a definição de uma classe.
Para que eu possa usar os métodos e os dados da classe cTarefa, eu preciso instanciar um objeto dessa classe. Em VDF há
duas formas de se fazer isso. A primeira é usando o bloco Object... End_Object. A segundo é usando um handle para
armazenar o retorno da função Create como você poderá notar em eventos posteriores.
Assim, como pode ser visto no código acima, embora oTarefa1 e oTarefa2 tenha sido criados a partir da mesma classe, eles
são objeto diferentes em si. Por exemplo, enquanto o método Get Dados de oTarefa1 retornará o valor 10 se for chamado,
o mesmo método de oTarefa2 retornará 15.
Hoje o uso de OOP é amplamente disseminado em nossa base de código, já que nossos projetos em sua maioria são
compostos de objetos Views, ModalPanels e ModalReports.
O Construtor da Instância O método Construct_Object é usado pelo VDF como um construtor do objeto, porém esse método não pode receber
argumentos como se dá com outras linguagens. Esse construtor é chamado no momento que a instancia é criada, sendo o
primeiro método a ser chamado. Ao instanciar um objeto, qualquer código que seja colocado dentro do bloco Object...
End_Object é considerado como complemento de Construct_Object. Assim, se algum valor default é alterado na
instanciação do objeto, essa alteração prevalecerá.
Outro fator é que o objeto é criado no momento em que Construct_Object é chamada. Nesse momento é que as instruções
dentro do bloco Object... End_Object são executadas. No caso de um objeto visual como um View ou ModalPanel é nesse
ponto que o objeto passa a existir e não a partir da ativação gráfica do objeto.
O VDF possui um segundo construtor chamado End_Construct_Object que é chamado logo após Construct_Object ser
concluído. Ao desenvolver usando classes herdadas onde futuras heranças podem afetar completamente o funcionamento
da classe, analise bem se End_Construct_Object pode servir com o intuito de evitar tais problemas.
O Destrutor da Instância Assim como a classe possui um construtor, a classe também possui um destrutor (cic.). Esse destrutor é usado para liberar o
espaço na memória que o objeto tem utilizado. O método que inicia a destruição é diferente do método que processa a
destruição do objeto. Portanto, quando desejamos destruir algum objeto nós chamamos o método Destroy para que isso
ocorra, mas qualquer processo que o objeto deve realizar na destruição (ex. liberação de memória, etc) deve ser feito dentro
do método Destroy_Object.
Atenção Destroy_Object é classificado como um evento disparado por Destroy e não deve ser chamado
em nenhuma parte do fonte. Ele será chamado automaticamente durante a destruição.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
3 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Caso você precise executar qualquer tarefa durante a destruição do objeto, use o método Destroy_Object porém realize
todas as operações antes do Forward desse método, visto que o objeto deixará de existir durante a execução do Forward
de Destroy_Object.
Atenção
A chamada para Destroy irá destruir o objeto com todos os seus objetos filhos, portanto, não é
necessário se preocupara em destruir objetos que foram criados e são filhos do objeto que você
está destruindo.
Conceitos
Obviamente, quando nós falamos de OOP, não estamos lidando com nenhum super-recurso que lhe entregará um aplicativo
feito. Técnicas de OOP só serão úteis se você entender porque usá-las e se for criativo o suficiente para isso. No geral, você
terá de assimilar poucas novas regras referentes OOP.
Nosso interesse em desenvolver usando OOP sempre será:
Reduzir a possibilidades de bugs
Possibilitar o reuso do código.
Diminuir o número de conhecimento necessário sobre um código para se trabalhar com ele.
Eliminar código desnecessário.
Automatizar e integrar processos.
Modular o fonte.
Para que isso ocorra, você deverá entender como certas regras funcionam, e mesmo que pareça desnecessário, use-as.
Vejamos como usar alguns conceitos básicos de OOP que são integrados ao VDF.
Nota
Em linguagens mais maduras como C++ e Java, os conceitos que explicaremos são muito mais
abrangentes, estamos cientes disso. Contudo, para um entendimento direto e descomplicado nós
explicamos tais conceitos da perspectiva da linguagem VDF. Assim, caso algo que você veja aqui
não esteja em conformidade com o conceito maior de OOP, lembre-se que esse manual é
direcionado para VDF.
Abstração Na ciência da computação, abstração é o processo em que dados e funções são definidos com uma representação similar ao
seu propósito com o fim de reduzir a quantidade de conhecimento necessário para usá-los. Por exemplo, quando você
chama o método Activate de uma instância da classe View muitos processos bem complexos são executados por detrás,
mas o desenvolvedor não precisará nem deverá verificar na integra o que exatamente a aplicação faz durante esse processo.
Tudo o que ele precisa é de uma documentação dizendo como o objeto deve ser usado.
Nesse caso particular da View, por exemplo, tudo o que essa classe faz é manipula APIs (funções em DLLs) do Windows afim
de criar uma janela modeless. No entanto, na API do Windows não há referência alguma a views ou a qualquer função
chamada Activate. Todo esse conceito foi desenvolvido para uma melhor interação com o código.
Assim, ao usar métodos como Get Field_Current_Value ou Find dos dicionários de dados, você não precisa ler o código que
define a classe de dicionário de dados para saber como usá-los, ou ter certezas que nenhum bug ocorrerá. A abstração traz
essa segurança para sua base de código.
Por outro lado, quando sua base de código é baseada em procedimentos, funções e dados globais, quando você altera ou
usa tais procedimentos, uma grande quantidade de código tem de ser novamente entendido para se certificar que nada
sairá errado.
Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar uma nova
classe, tente por em prática esse conceito com as seguintes sugestões.
Use nomes para classes, métodos e dados que indique qual é a funcionalidade de cada um.
Desenvolva bem para que todos que usem a sua classe, o que incluem você, possam usá-la sem entendê-la
internamente.
4 Fascículos do Desenvolvedor
Desenvolva pensando em futuras alterações e prováveis alterações no conteúdo dessa classe.
Use métodos para ler, alterar e gravar os dados (em VDF geralmente são property’s) ao invés de expô-los para uso,
isso facilitará qualquer necessidade de regras para captura e alteração desses dados. (Em VDF métodos Get são
vistos como propriedades, mas são métodos)
Pense de maneira abstrata (ao desenvolver coisas, pense em figuras como grades, motores, etc, e os nomeie assim).
Divida as classes em camadas de abstração diferentes criando diferentes classes para diferentes funções e
agrupando essas camadas em outras classes que representem conceitos abstratos mais amplos.
Separe camadas de abstração de baixo nível (camadas que lidam com hardware, recursos do OS, cálculos básicos,
gerenciamento de dados) das camadas de alto nível (camadas que lidam com banco de dados, informações
operativas do software, unificação de diversas funções básicas.)
Faça o complexo, entretanto, o apresente como simples. Decida quais detalhes são importantes e devem ser
expostos.
O uso de abstração promove técnicas de reuso de código, disciplina e segurança. Mas é claro que para fazer pleno uso dos
benefícios da abstração é necessário entender outros aspectos de OOP.
Encapsulamento Encapsulamento é a técnica que promove o isolamento de funções e procedimentos a certos dados específicos. Assim, ao
desenvolver usando o conceito de cápsulas, nós sempre levamos em conta o que deverá ser alterado de dentro da classe, e
o que deverá ser alterado de fora da classe.
Em VDF, parte dessa política de segurança funciona. Os métodos e dados das classes que estamos criando podem ser
definidos como Private ou Public. Os dados e métodos privados devem ser usados em uso interno e nunca serem exposto
fora do contexto da classe, enquanto os métodos e dados públicos são usados tanto interna como externamente.
O código abaixo indica se um método é privado ou público.
Class cTarefa is a cObject
Procedure Construct_Object
Forward Send Construct_Object
{ Visibility=Private}
Property Integer piDados
End_Procedure
// Fonte continua...
End_Class
Como demonstrado nesse exemplo, a propriedade piDados foi definida como privada por meio da tag Visibility.
Obviamente, VDF não possui um nível avançado no controle do encapsulamento que impeça, por exemplo, que esses dados
ou métodos privados não possam ser acessados fora do contexto.
No entanto, ao usarmos essa tag, indicando que o método ou dado é privado, o IDE do VDF o descarta no intelli-code e é
dada uma indicação ao próximo programador o que exatamente é privado e não deve ser usado de fora da classe.
Esse recurso pode ser útil também caso no futuro nós precisemos usar geradores automáticos de documentação.
Nota
Um exemplo clássico de mau uso de métodos privados são as chamadas de Deactivate_Group.
Esse método deveria ser usado apenas pela própria classe, mas por algum motivo alguns
programadores o usam para desativar janelas.
Herança A herança é uma das características fundamenteis para o melhor reuso de código quando se programa usando OOP. O
conceito de herança é bem simples: Nós criamos uma classe usando a planta de outra classe fazendo alterações nessa nova
classe para as características que desejamos.
Nesse processo, a classe que serve como molde geralmente é chamada de Classe Base ou Superclasse. A partir daí, a classe
que nós criamos com o molde de outra classe é chamada de Classe Derivada ou Sub-Classe.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
5 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Para entendermos o conceito de herança, de uma olhada na figura a seguir onde cada quadro representa uma classe.
Como você pode ver na figura acima, as classes Carro, Moto e Carreta derivam da superclasse Veículo. A superclasse nesse
caso aqui foi criada não porque ela seria usada diretamente, mas aqui ela está presente apenas para que o código em
comum das classes Carro, Moto e Carreta não tenham de ser reescrito em cada uma delas. Muitas vezes, a superclasse nunca
será instanciada, sendo considerada como uma classe abstrata.
A figura a seguir demonstra o resultado do uso de herança para a sub-classe Asfalto.
Como você pode ver, a sub-classe moto recebe características da classe veículo a adiciona novas características. Por fim, a
classe asfalto deriva de Moto herdando as características das duas superclasses em sequencia.
Obviamente, não é possível apenas adicionar métodos às subclasses, mas também é possível anular, substituir e incrementar
métodos já existentes.
Demonstração prática em VDF
O código abaixo mostra como criar a superclasse Veículo em VDF. (O exemplo está resumido)
Class cVeiculo is a cObject
Procedure Construct_Object
Forward Send Construct_Object
{ Visibility=Private}
Property Number pnPeso
// Fonte continua...
End_Procedure
Procedure SetPeso Number nPeso
Set pnPeso to nPeso
End_Procedure
// Fonte continua...
End_Class
Uma vez que a superclasse foi criada com o código em comum que precisamos em outras classes nós começamos o
processo de derivação. O próximo código demonstra a criação das subclasses.
Veículo
Carro
Utilitário Sedã Esportivo
Moto
Estrada Asfalto Cidade
Carreta
Dois Trens Três Trens
Veículo
•SetPeso
•Preço
•Combustível
•Fabricante
•Qtd. Rodas
•Motor
Moto
•Velocidade
•Tipo Suspensão
•Descarga
Asfalto
•Aditivo Pneus
•GPS
6 Fascículos do Desenvolvedor
Class cMoto is a cVeiculo
Procedure Construct_Object
Forward Send Construct_Object
{ Visibility=Private}
Property Number piSuspencao
// Fonte continua...
End_Procedure
Procedure SetPeso Number nPeso // Sobrecarga do método SetPeso de cVeiculo
Forward Send SetPeso nPeso // Aqui executa o SetPeso da superclasse
Set piSuspensao to ((pnPeso(Self)) * 10)
End_Procedure
// Fonte continua...
End_Class
Class cAsfalto is a cMoto
Procedure Construct_Object
Forward Send Construct_Object
{ Visibility=Private}
Property String psGPS
// Fonte continua...
End_Procedure
Procedure SetPeso Number nPeso // Sobrecarga do método SetPeso de cVeiculo
Set pnPeso to (nPeso / 2)
Set piSuspensao to 1000
End_Procedure
// Fonte continua...
End_Class
Como você pode ver no exemplo dado, os dados das superclasses foram herdados pela subclasse cAsfalto. Outro fator
interessante é que o método SetPeso criado pela superclasse cVeiculo foi complementado dentro de cMoto. Isso porque o
Forward serve para indicar que o código da próxima superclasse deve ser executado naquele ponto.
Já na classe cAsfalto o tratamento para SetPeso foi um pouco diferente. As instruções criadas em cVeiculo e cMotos foram
completamente descartadas e um novo tratamento foi dado. Se a declaração Forward não for feita dentro do método que
está sendo sobrecarregado, a versão da superclasse para o método não é executada.
Construtores e Destrutores
Como você pode ver no exemplo dado no código anterior, dentro do construtor Construct_Object de cMoto há uma
declaração Forward para o construtor da superclasse de cMoto, no caso, cVeiculo. Esses Forwards são obrigatórios já que é
dentro do construtor que as propriedades são criadas. Assim, sempre coloque o Forward para o construtor logo no inicio.
A classe cObject
Em VDF todas as classes são criadas derivando-se de outra classe. Quando não há uma classe específica que nós desejamos
usar como superclasse, cObject passa a ser usado como classe base. Isso porque é dentro de cObject que a definição dos
construtores e destrutores é feita, além de algumas outras informações adicionais.
Herança Múltipla Em VDF nós fazemos a herança apenas a partir de uma classe, mas em dados momentos seria extremamente útil pode
mesclar a nossa classe herdada com outras classes. Embora o conceito de heranças múltiplas seja muito mais abrangente em
outras linguagens, há meios satisfatórios de se alcançar esse conceito.
Por exemplo, suponhamos que nós tivéssemos uma classe chamada brinde e desejássemos que o código dela pudesse ser
usado na classe cAsfalto e na classe cEstrada apenas, alem de termos essa mesma classe disponível para outras tarefas não
relacionadas com essas classes derivadas de cVeiculo. Para entender a idéia, dê uma olhada na figura a seguir.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
7 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Essa figura retrata bem a maneira que o VDF trata essa situação. Só é possível ter uma classe base em VDF, ou superclasse.
No nosso caso, a classe Moto sempre será a classe base para os dois casos. Porém, isso não nos impede de mesclar a
interface (dados e métodos) de outra classe na nossa.
Essa mixagem é possível, mas ao trabalhar com ela você lidará com algumas regras básicas referentes à classe que terá sua
interface inserida (ex. Brinde) na classe mesclada (ex. Asfalto).
A classe para mixagem deverá sempre herdar da classe Mixin ou de alguma classe que herde dela.
O construtor de uma classe de mixagem é um procedimento chamado Define_ mais o nome da própria classe.
A chamada para esse construtor deverá ser colocada na classe destino logo após ela ter envida o Forward para
Construct_Object da classe destino.
A classe para mixagem não é uma superclasse da classe mesclada, o que significa que você não poderá usar
sobrecargas para métodos, ou Forwards como acontece no caso da superclasse.
Certifique-se que a classe para mixagem não possui métodos e funções com os mesmo nomes dos métodos da
classe mesclada.
Use nomes extensos para dados e métodos na classe de mixagem para evitar ambiguidades.
O comando Import_Class_Protocol precisa estar no corpo da classe mesclada fora de qualquer método.
O código a seguir demonstra como aplicar essas sugestões baseado na situação da figura anterior.
Class cBrinde is a Mixin
Procedure Define_cBrinde
{ Visibility=Private}
Property String psDescricaoBrinde
End_Procedure
Procedure SetDescricaoBrinde String sBrinde
Set psDescricaoBrinde to (Trim(sBrinde))
End_Procedure
// Fonte continua...
End_Class
Class cAsfalto is a cMoto
Procedure Construct_Object
Forward Send Construct_Object
Send Define_cBrinde
{ Visibility=Private}
Property String psGPS
// Fonte continua...
End_Procedure
Import_Class_Protocol cBrinde
// Fonte continua...
End_Class
Composição Assim como é possível declarar propriedades dentro do construtor da classe, há uma outra técnica que deve ser utilizada
com certa cautela, mas que é muito proveitosa: a composição de objetos.
Asfalto
Brinde
Moto
Registro
Estrada
Brinde Moto
8 Fascículos do Desenvolvedor
Objetos do mundo real derivam características, ou herdam, de um objeto base. Porém, a grande maioria dos objetos do
mundo real são construídos usando-se outros objetos. O mesmo conceito pode ser aplicado a OOP.
Da mesma forma que você faz declaração de propriedades que funcionarão como dados no construtor Construct_Object da
classe, também é possível criar objetos inteiros. O construtor é o método mais utilizado para composição.
A composição de objetos é muito comum principalmente em casos onde se declara um objeto array dentro da classe em
questão para uso posterior. O exemplo a seguir demonstra a realização da composição.
Class cProduto is a Mixin
Procedure Define_cBrinde
{ Visibility=Private}
Property String psDescricaoBrinde
Object oParcelas is an Array
End_Object
End_Procedure
// Fonte continua...
End_Class
Nesse exemplo dado, para cada instância de cProduto criada, haverá um objeto oParcelas disponível criado durante a
construção do objeto. Isso significa que se nós tivermos cinquenta instâncias de cProtudo, nós teremos cinquenta objetos
oParcela para essas instâncias.
Embora a composição de objetos faça parte do completo conceito OOP, tenha sempre bastante cuidado ao escrever classes
que possuem objetos internos a fim de não acabar mesclando funções que deveriam estar isoladas. Sempre verifique se o
objeto que você pretende compor na sua classe não pode ser passado como parâmetro ou referenciado por alguma
propriedade.
Instanciação de Objetos
Após terminar de por em prática todos os conceitos citados e de concluir a sua classe, prepare-se para usar os objetos que
essa classe pode gerar. A instanciação de objetos é bem simples. Na verdade, há duas formas de fazê-la.
A primeira é usando um bloco Object... End_Object como mostra o código abaixo.
Object oOK is a Button
Set GuiSize to 50 50
End_Object
O mesmo código citado na primeira forma de instanciação pode ser convertido para a segunda forma de instanciação.
Handle hbtOK
Get Create U_Button to hbtOK
Set GuiSize Of hbtOK to 50 50
Como você pode comparar nos dois exemplos, a diferença é que a primeira forma de instanciação se ocupa com um nome
que possa ser usado facilmente pelo código. Contudo, a segunda forma de instanciação ocupa-se apenas com o
manipulador, o número identificador do objeto, para referenciá-lo.
Ambas as formas tem as suas utilidades. A primeira sendo mais comum para objetos estáticos e a ultima mais comum para
criação de objetos dinâmicos. O valor armazenado no handle no segundo exemplo poderia ser encontrado no primeiro
exemplo dentro da propriedade Object_ID que todos os objetos cujas classes derivam de cObject possuem.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
9 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Capítulo 2
Abordagem Geral sobre relatórios e seu Desempenho
Relatórios são elementos de alto nível de uma aplicação que envolvem grande pare do operacional do aplicativo. Embora
muito do desenvolvimento de relatórios esteja relacionado com filtro de informações e layout de impressão, todo esse
processo faz parte do desenvolvimento em VDF e há muito que pode ser feito para padronizar e melhorar os resultados.
Os relatórios devem seguir uma estrutura bem simples onde apresentem características similares uns aos outros com o fim
de elevar a velocidade que os procedimentos são feitos e permitir um grande nível de flexibilidade. Esse capítulo descreve
boas práticas no desenvolvimento de relatórios para o Integra.
Nota Informações referentes à capacidade dos processos de responder a pedidos do usuário serão
dadas em um fascículo futuro.
O procedimento IniciarRelatorio
Geralmente, ao trabalhar com relatórios, você encontrará um procedimento que terá esse nome ou algo similar a esse. Esse
procedimento deve ser utilizado como o gatilho para a geração de relatórios. Ou seja, somente através dele deve ser
possível a geração do relatório, e todas as outras funções submetidas a ele devem ser consideradas como dependências
desse procedimento, sendo chamadas apenas através de IniciarRelatório.
O procedimento IniciarRelatório é responsável basicamente por:
Iniciar as tabelas temporárias.
Preparar a barra de progresso.
Decidir se a impressão será feita através do BasicReport ou do Crystal Reports e informar os dados necessários para
que o gerador escolhido possa ser usado.
Verificar se há uma impressora configurada para a impressão (se necessário).
Trancar as tabelas que não sofrerão alterações antes de começar a reunir dados e destrancá-las após a geração dos
dados.
Preparar e executar o objeto oProcessoRelatório para que os dados sejam reunidos enquanto as tabelas estão
trancadas, processados e exibidos (Isso quando se usa o Crystal Reports).
Exibir uma tela de aguarde no caso de relatórios que não precisam usar BusinessProcess.
Em alguns casos, esse procedimento também possui um parâmetro chamado iModelo que recebe dois valores,
RELATORIO_COMUM ou RELATORIO_TIMBRADO, com a finalidade de decidir se o relatório deverá ser impresso através
do BasicReport ou do Crystal Reports respectivamente.
Seria apropriado que não fosse incluído no conteúdo de IniciarRelatório:
Troca de dados por array. Antes, todos os dados deveriam ser passados por parâmetro ou deveriam simplesmente
estar nos controles visuais do relatório. Sempre dê preferência a passar os dados por parâmetros quando necessário
tomar uma decisão.
Processamento de informações que deveriam ser reunidas por Gera_Dados ou pelo objeto do BasicReport.
Comandos Procedure_Return que impeçam que o fim do procedimento sempre seja executado.
Chamadas para funções ou procedimentos que não pertencem ao relatório em que estamos trabalhando, exceto
quando necessário.
Chamada para um procedimento IniciarRelatório de outro relatório que também usa o sistema de trancas. Caso,
isso aconteça, erros poderão surgir.
10 Fascículos do Desenvolvedor
Para alcançar o seu propósito o procedimento IniciarRelatório geralmente trabalha em conjunto com um gerador de
relatórios. Caso você tenha de usar o BasicReport, você conseguirá proceder corretamente, por chamá-lo durante a tranca
das tabelas e por se certificar que as tabelas sejam destrancadas no fim do relatório. (Esclarecimentos adicionais sobre essa
estrutura serão dados.)
O Procedimento Gera_Dados O procedimento Gera_Dados é encontrado em diversos relatórios. Esse procedimento deve ter como objetivos:
Buscar as informações dos controles visuais do relatório que serão usadas na geração do relatório como base para
filtros.
Varrer as tabelas envolvidas a partir desses dados, encontrando assim todos os dados desejados no banco de
dados, se valendo de filtros.
Incrementar a barra de progresso (Quando o modelo de relatório usa uma).
Gravar esses dados em uma tabela temporária que fora iniciada no método IniciarRelatório.
Geralmente esse processo pode ser mais complexo e envolver mais de uma busca ou mais de uma tabela temporária.
Mesmo assim, essa sempre será a estrutura básica.
Os seguintes pontos abaixo são recomendações para que o código se mantenha flexível e mais legível quanto ao
procedimento Gera_Dados.
Não deve ser uma função.
Não deve receber parâmetros.
Não deve conter linhas no final do procedimento que devem ser executadas por padrão de modo que se possa
usar Procedure_Return em qualquer parte do código (Por exemplo, quando se cria um DDO dentro de Gera_Dados,
esse objeto precisará ser destruído no final de todo o processo, não sendo possível o uso indiscriminado de
Procedure_Return para cancelamento do processo em qualquer estágio em que ele esteja).
Deve evitar receber dados por array.
Não deve conter instruções para o gerador de relatórios, não importa qual seja.
Não deve ter seus processos divididos em sub-processos, a menos que extremamente necessário (Por exemplo,
chamadas para outro procedimento que grava os dados em um temporário, ou algo do tipo).
Esses passos devem ser seguidos para que se evite complicações maiores para se explorar a flexibilidade do fonte.
Gerando Relatórios
A decisão básica na criação de um relatório é decidir qual gerador ele irá usar: Crystal, Basic ou ambos. Também, quanto se
trata de relatórios responsáveis por atualizar a base de dados, deve-se levar em conta fatores relacionados a segurança,
como por exemplo o uso de transações. Outra característica que representa um problema na geração de relatórios é o
acesso à dados de tabelas temporárias.
A partir de agora nós demonstraremos como lidar com essas facetas com alguns exemplos.
Usando Relatórios do Crystal Reports Nesse exemplo nós exibimos a geração de um relatório comum do Crystal. No procedimento IniciarRelatório, as linha
iniciais são responsáveis por iniciar as tabelas temporárias e preparar a barra de progresso. Após isso são marcadas as
tabelas que sofrem alterações durante a geração e exibição dos dados, e todas as outras são trancadas.
Nota Informações sobre tranca de tabelas serão dadas em um fascículo futuro.
Nesse ponto nó podemos iniciar qualquer processo relacionado a manipulação de dados, pois somente as tabelas que
sofrerão alterações estão destrancadas. Assim, para dar inicio a geração dos dados, nós preparamos o objeto
oProcessoRelatorio por meio do procedimento Reset, informando Gera_Dados como o procedimento responsável pela
geração dos dados, FinalSucesso como o procedimento que será chamado caso Gera_Dados tenha sucesso e
FinalCancelado para tratar caso algo tenha cancelado o processo de geração dos dados. Uma vez feito isso, chamamos o
procedimento DoProcess para começar finalmente a reunir os dados.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
11 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Abaixo de DoProcess nós colocamos o que nós queremos que seja executado independente de a geração e exibição do
relatório ter dado certo ou não. O que sempre será necessário é voltar as permissões de todas as tabelas ao normal. Assim
nós sempre chamamos no final de tudo o método RestauraFileMode.
// Função ilustrativa...
Procedure FinalSucesso
Send Completa_Progresso
Send Run_Report to (oRelatorio(Self))
End_Procedure
// Função ilustrativa...
Procedure FinalCancelado
// Faz com que a barra de progresso fique vazia como no inicio.
Set piPosition of (Progresso(Destino(Self))) to 0
End_Procedure
Procedure IniciarRelatorio
Send OnProcess to (RsysVen_Bussines_Flex(Self))
Send OnProcess to (RsysVenProduto_Bussines_Flex(Self))
Send OnProcess to (RsysVenparcela_Bussines_Flex(Self))
Send OnProcess to (RsysVenAtributos_Bussines_Flex(Self))
Send Ativa_Progresso "FORMAR.File_Number"
Send LimpaTabelas of (oOpenModeControler(Self))
Send AdicionaTabela of (oOpenModeControler(Self)) TMP1000.File_Number
Send AdicionaTabela of (oOpenModeControler(Self)) TMP999.File_Number
Send ConfiguraReadOnly of (oOpenModeControler(Self))
Send Reset of (oProcessoRelatorio(Self)) (RSysVen(Self)) Msg_Gera_Dados Msg_FinalSucesso ;
Msg_FinalCancelado True
Send DoProcess to (oProcessoRelatorio(Self))
Send RestauraFileMode of (oOpenModeControler(Self))
End_Procedure
Procedure Gera_Dados
String ddata
Boolean bGiro
Get Value of (relatorio(Self)) to srelatorio
get Value of (ordem(Self)) to sordem
Clear FORMAR
Repeat
Send Incrementa_Progresso
Find GT formar by index.1
Move (found) to bgiro
if (bgiro) Begin
Move (Right((Append("000000",formar.marca)),6)) to scodigo01
Clear 1000
Set_Field_Value 1000 01 to scodigo01
Set_Field_Value 1000 06 to FORMAR.DESCRICAO
if (sordem = "Código") Set_Field_Value 1000 31 to scodigo01
if (sordem = "Descrição") Set_Field_Value 1000 31 to FORMAR.DESCRICAO
saverecord 1000
end
Send Update_Status of (oProcessoRelatorio(Self)) ;
("Registros Processados: " + (String(igTotalRegistros)))
if (Cancel_Check(oProcessoRelatorio(Self)) <> 0) Procedure_Return 1
If (bgiro) Loop
End_Procedure
Relatório modal que utiliza o BasicReport e o Crytal Reports Há muitos relatórios modais sendo utilizados pelo Integra atualmente. Esses relatórios geralmente possuem suporte para
impressões através do BasicReport ou do Crystal Reports. Nesses casos nós temos uma situação especial. Alguns desses
relatórios devem ser capazes de detectar quais dos dois geradores devem ser usados através da seguinte lógica:
Se nas configurações dos relatórios é detectado que ambos os geradores podem ser usados, exibe a janela modal
com dois botões cujos labels são Comum e Timbrado (há também um botão cancelar).
12 Fascículos do Desenvolvedor
Se for detectado que apenas um dos relatórios está configurado, imprime esse relatório sem exibir a janela modal
do relatório citada no primeiro passo.
Se for detectado que nenhum dos geradores está configurado, exibe uma mensagem pedindo o usuário para
configurar um modelo.
Nessas situações onde a escolha do usuário pode interferir na seleção do gerador do relatório, é apropriado que o
procedimento IniciarRelatório tenha o parâmetro iModelo. Assim nós apenas mudamos o parâmetro no conteúdo dos
botões que iniciam os relatórios.
Na função Activate do diálogo modal nós colocamos instruções que receberão os dados necessários para a geração do
relatório (Geralmente esses dados serão passados via SPG, SRG ou GDM. Para mais informações consulte o Fascículo I,
Capítulo 1 – Trabalhando com Janelas Visuais Deferred).
Atenção. Para trabalhar com diálogos modais seria interessante que você lesse o Fascículo I, Apêndice II – Ativação e
Desativação de Janelas – O Método Sugerido.
Abaixo segue um exemplo do como proceder na inicialização do objeto modal.
Cd_Popup_Object RelatorioAtual is a ReportPanel
//...
Object BtnComum is a Button
Set Label to "C&omum"
Set Size to 15 60
Set Location to 55 7
Cursor_BtnNext
Procedure OnClick
Send IniciarRelatorio To (RelatorioAtual _CD(Self)) RELATORIO_COMUM
Send Limpa_Tela To (RelatorioAtual _CD(Self))
If (Active_State(RelatorioAtual _CD(Self))) Send Close_Panel to (RelatorioAtual _CD(Self))
// Active_State é verificado para se certificar que Close_Panel só seja enviado se a
// janela estiver sendo exibida.
End_Procedure
End_Object
Object BtnTimbrado is a Button
Set Label to "&Timbrado"
Set Size to 15 60
Set Location to 55 7
Cursor_BtnNext
Procedure OnClick
Send IniciarRelatorio To (RelatorioAtual _CD(Self)) RELATORIO_TIMBRADO
Send Limpa_Tela To (RelatorioAtual _CD(Self))
If (Active_State(RelatorioAtual _CD(Self))) Send Close_Panel to (RelatorioAtual _CD(Self))
// Active_State é verificado para se certificar que Close_Panel só seja enviado se a
// janela estiver sendo exibida.
End_Procedure
End_Object
Procedure Activate
Send Limpa_Tela of (RelatorioAtual_CD(Self)) // Chame sempre antes.
Integer iResult iFilial
Get LerRegistro of (oRecursosGlobais(Self)) REG_RESTORC_FILIAL (&iFilial) to iResult
If (OK_ = iResult) Begin
Set Value of (Filial(Selecao(Self))) to iFilial
Get RemoverRegistro of (oRecursosGlobais(Self)) REG_RESTORC_FILIAL to iResult
End
// Aqui está o teste que verifica se o relatório deve ser exibido ou não.
If (CADREL.ORCAMENTO_C = "" or CADREL.ORCAMENTO_D = "") Begin
If (CADREL.ORCAMENTO_C > "") Send onClick to (BtnComum(Self))
Else If (CADREL.ORCAMENTO_D > "") Send onClick to (BtnTimbrado(Self))
Else Send Stop_Box ("Por favor, configure o modelo de relatório no Cadastro de " +
"Relatório!") "Não foi possível imprimir o relatório"
Procedure_Return 1 // Cancela a exibição do relatório.
End
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
13 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Forward Send Activate
End_Procedure
Uma vez que a já temos o cenário de como iniciar esse tipo de relatório, agora trataremos do procedimento IniciarRelatório
e as tarefas que ele deve cumprir.
Nesse tipo de relatório, geralmente não há barras de progresso, o que torna o uso de BusinessProcess um tanto
desnecessário. Muito mais prático é o uso da tela de Aguarde, que será explicada em mais detalhes posteriormente. Caso
você prefira não usar esse modelo você pode fazer uma combinação desse procedimento InicarRelatório usando o
BusinessProcess para o Crystal e para o BasicReport.
Nota Informações sobre Business Process serão dadas em um fascículo futuro.
Procedure IniciarRelatorio Integer iModelo
Integer iResult
Send Tela_Aguarde "Por favor, aguarde a impressão do relatório."
If (Window_Handle(RelatorioAtual_CD(Self)) <> 0) ;
Move (EnableWindow(Window_Handle(RelatorioAtual_CD(Self)), False)) to iResult
Send LimpaTabelas of (oOpenModeControler(Self))
Send AdicionaTabela of (oOpenModeControler(Self)) TMP1000.File_Number
Send ConfiguraReadOnly of (oOpenModeControler(Self))
FindKey Eq CadMaq By Index.1 For CADMAQ.ESTACAO EQ sgEstacao
FindKey Eq CadImp By Index.1 For CADIMP.ID EQ CADMAQ.IMP_RELATORIO
If (Found) Begin
If (iModelo = RELATORIO_TIMBRADO) Begin
Send OnProcess to (Relatorio_Business_Flex(Self))
Send Gera_Dados
Send Run_Report to (rcadrec_crystal(Self))
End
Else If (iModelo = RELATORIO_COMUM) Begin
If ((Left(CadImp.Caminho,3)) <> 'LPT') Begin
Set Output_Device_Name To (Trim(CADIMP.CAMINHO))
Send Run_Report To (Relatorio_BasicReport(Self))
Close_Output (Trim(CadImp.Caminho))
End
Else Begin
Set Output_Device_Name To (sgCrystalPath + "\rel.txt")
Send Run_Report To (Relatorio_BasicReport(Self))
Close_Output (Trim(CadImp.Caminho))
Send Print_File (sgCrystalPath + "\rel.txt")
End
End
End
Else Send Stop_Box "A Impressora não pode ser Encontrada !" "Erro no Cadastro de Estação"
Send RestauraFileMode of (oOpenModeControler(Self))
If (Window_Handle(RCadRec_CD(Self)) <> 0) ;
Move (EnableWindow(Window_Handle(RCadRec_CD(Self)), True)) to iResult
Send Deactivate to (Aguarde(Self))
End_Procedure
Assim como você precisa adicionar linhas no loop principal durante a geração do relatório quando você usa um
BusinessProcess, você também deverá adicionar a instrução abaixo no loop principal da função Gera_Dados e no final do
Procedure_Section Body do objeto BasicReport que você estiver usando.
Send ProcessEvents To (Aguarde(Self)) // Mantém a aplicação responsiva
Usando Transações em relatórios Alguns relatórios reúnem um grupo de dados, e com base nesses dados aplicam alterações em tabelas da Base de Dados
que não são temporárias. Essas operações devem ser feitas de modo seguro por meio de transações, diferente das
operações de criação, modificação e exclusão em tabelas temporárias.
14 Fascículos do Desenvolvedor
Quando for necessário criar uma transação para controlar modificações nas tabelas da Base de Dados principal, tenha
sempre as seguintes considerações em mente:
Sempre coloque o código responsável pela alteração sob transações explicitas.
Na geração do relatório sempre coloque transações explicitas no procedimento FinalSucesso.
Use a tela de aguarde para manter o relatório responsivo.
Use os próprios dados da tabela temporária para decidir quais registros devem ser alterados na BD Principal.
Caso você resolva usar contêineres, crie e remova o contêiner dentro de IniciarRelatório antes de chamar
DoProcess e após respectivamente.
Caso os itens do contêiner sejam referências a outros objetos (ex: um Array) certifique-se de excluir esses objetos
antes de destruir o contêiner. Essa exclusão deve acontecer fora de um procedimento sob transação.
Nota Informações sobre uso de transações e reread serão dadas em um fascículo futuro.
Segue um exemplo de um relatório que usa transações para alterar a DB Principal.
// Função ilustrativa...
Procedure FinalSucesso
Integer iCliente
Clear TMP1000
Find GT TMP1000 by Recnum
Send Tela_Aguarde "Gravando Situação de Clientes"
Begin_Transaction
While (Found)
Get_Field_Value 1000 1 to iCliente
FindKey EQ CADCLI by Index.1 for CADCLI.CLIENTE eq iCliente
If (Found) Begin
Move (Value(Situacao(Self))) to CADCLI.SITUACAO
Move CADSYS.DATA_SISTEMA to CADCLI.DTSITUACAO
Move "S" to CADCLI.ENVIA_POCKET
Move CADSYS.DATA_SISTEMA to CADCLI.DTEXPORTACAO
SaveRecord CADCLI
End
Send ProcessEvents of (Aguarde(Self))
Find GT TMP1000 by Recnum
Loop
End_Transaction
Send Deactivate to (Aguarde(Self))
Send Completa_Progresso
Send Run_Report to (oRelatorio(Self))
End_Procedure
// Função ilustrativa...
Procedure FinalCancelado
// Faz com que a barra de progresso fique vazia como no inicio.
Set piPosition of (Progresso(Destino(Self))) to 0
End_Procedure
Procedure IniciarRelatorio
Send OnProcess to (TMP100_CadCli_BusinessFlex(Self))
Send Ativa_Progresso "CADCLI.File_Number"
Send LimpaTabelas of (oOpenModeControler(Self))
Send AdicionaTabela of (oOpenModeControler(Self)) TMP1000.File_Number
Send AdicionaTabela of (oOpenModeControler(Self)) CADCLI.File_Number
Send ConfiguraReadOnly of (oOpenModeControler(Self))
Send Reset of (oProcessoRelatorio(Self)) (RSysVen(Self)) Msg_Gera_Dados Msg_FinalSucesso ;
Msg_FinalCancelado True
Send DoProcess to (oProcessoRelatorio(Self))
Send RestauraFileMode of (oOpenModeControler(Self))
End_Procedure
Procedure Gera_Dados
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
15 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
String ddata
Boolean bGiro
Get Value of (relatorio(Self)) to srelatorio
get Value of (ordem(Self)) to sordem
Clear FORMAR
Repeat
Send Incrementa_Progresso
Find GT CADCLI by index.1
Move (found) to bgiro
if (bgiro) Begin
Move (Right((Append("000000",CADCLI.CLIENTE)),6)) to scodigo01
Clear 1000
Set_Field_Value 1000 01 to scodigo01
Set_Field_Value 1000 06 to CADCLI.NOME
if (sordem = "Código") Set_Field_Value 1000 31 to scodigo01
if (sordem = "Descrição") Set_Field_Value 1000 31 to CADCLI.NOME
saverecord 1000
end
Send Update_Status of (oProcessoRelatorio(Self)) ;
("Registros Processados: " + (String(igTotalRegistros)))
if (Cancel_Check(oProcessoRelatorio(Self)) <> 0) Procedure_Return 1
If (bgiro) Loop
End_Procedure
Acessando colunas pelos seus nomes Uma característica presente na geração de relatórios é uso de tabelas temporárias. Essas tabelas permitem ao programador
a flexibilidade de modelá-las segundas as necessidades presentes no relatório. Em alguns casos, durante a geração do
relatório é necessário realizar consultas em tabelas temporárias, para alimentar outras tabelas temporárias. Uma vez que as
tabelas temporárias não possuem uma estrutura pré-definida, acessar colunas pelos seus nomes se torna impossível em VDF,
sendo necessário referenciar as colunas pelos seus respectivos números.
Porém, muitas vezes isso reduz a legibilidade do fonte, e pode ser facilmente contornado por se usar a classe
cSlotTabelaDinamica. Tudo o que você precisa fazer é:
Criar um objeto cSlotTabelaDinamica.
Informar FONTE_CAMPO_TAG como valor para a propriedade piFonteCampos desse objeto.
Chamar IniciarMetaDadosTabela informando o número da tabela temporária e uma string única para a
identificação desses metadados para esse relatório em um gerenciador.
Usar a função CampoValorCorrente para retornar o valor do campo segundo o nome da coluna informada.
Destruir o objeto cSlotTabelaDinamica quando terminar.
Ao fazer isso, você deve estar atento ao valor da string de identificação dos metadados. Essa string que é passada para
IniciarMetadaDadosTabela logo após a criação do objeto é usada para que o nome das colunas da tabela TMP1000 sejam
armazenados na memória apenas uma vez. Ou seja, caso você tenha que iniciar esses dados novamente,
cSlotTabelaDinamica recorrerá há uma lista que já existe. Porém, quando se trata de temporários, cada relatório possui uma
formação diferente de colunas. Assim, cada relatório deve passar uma string diferente, para que os metadados de cada
relatório para a tabela TMP1000 sejam armazenados separadamente.
O valor dessa string, portanto deverá vir com o nome da tabela (Ex. TMP1000) mais um underscore (_) e o nome do módulo
que a usa (Ex. RCLICOB). Se possível, use uma constante de compilação.
Procedure FinalSucesso
Send Tela_Aguarde "Gravando Cadastros de Clientes"
Handle hcstdTMP1000
Get Create U_cSlotTabelaDinamica to hcstdTMP1000
Set piFonteCampos of hcstdTMP1000 to FONTE_CAMPO_TAG
Send IniciarMetaDadosTabela of hcstdTMP1000 TMP1000.File_Number "TMP1000_RCLICOB"
Begin_Transaction
Clear TMP1000
Set_Field_Value (phTabela(hcstdTMP1000)) (Campo(hcstdTMP1000, "ID")) to 1
16 Fascículos do Desenvolvedor
Find GT TMP1000 by Index.1
While (Found)
FindKey EQ CLIDOC by Index.1 for ;
CLIDOC.FILIAL EQ (Integer(CampoValorCorrente(hcstdTMP1000,"FILIAL"))) ;
CLIDOC.CLIENTE EQ (Integer(CampoValorCorrente(hcstdTMP1000,"CLIENTE")));
CLIDOC.CONTRATO EQ (Integer(CampoValorCorrente(hcstdTMP1000,"CONTRATO")));
CLIDOC.PARCELA EQ (Integer(CampoValorCorrente(hcstdTMP1000,"PARCELA")))
Move (Value(Cobrador(GrCobradores(Self)))) to CLIDOC.COBRADOR
Move (Value(DtCobrador(GrCobradores(Self))))to CLIDOC.DT_COBRADOR
Move CADSYS.DATA_SISTEMA to CLIDOC.DATA_ALT
Get fMonta_Hora to CLIDOC.HORA_ALT
Move igUsuario to CLIDOC.USU_ALT
Move sgEstacao to CLIDOC.MAQ_ALT
Move "S" to CLIDOC.ENVIA_POCKET
Move CADSYS.DATA_SISTEMA to CLIDOC.DTEXPORTACAO
Get CampoValorCorrente of hcstdTMP1000 "Situacao" to CLIDOC.SITUACAO
SaveRecord CLIDOC
Find GT TMP1000 by Index.1
Send ProcessEvents of (Aguarde(Self))
Loop
End_Transaction
Send Destroy to hcstdTMP1000
Send Deactivate of (Aguarde(Self))
Send Run_Report to (oRelatorio(Self))
Send Completa_Progresso
End_Procedure
// Fonte continua...
Como você pode notar no exemplo acima, a manipulação da tabela TMP1000 se tornou muito mais simples. A função
CampoValorCorrente, permitiu se consultar a valor de um determinado campo pelo nome dele mesmo sem precisar de se
copiar valores para variáveis, como aconteceria de costume. A propriedade phTabela foi usada para retorna o número da
tabela, como valeria para o campo File_Number. A função Campo foi usada para retorna o número do campo segundo o
nome do campo informado. Assim, o código foi simplificado para leitura e escrita sem se sacrificar o desempenho.
Atenção Desde que não se reserve slots do banco de dados para se ler tabelas paralelamente, não há
empecilho algum no uso de cSlotTabelaDinamica com operações como Reread.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
17 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Capítulo 3
Método Padrão para Comentários
Desenvolver aplicativos é uma tarefa que pode ser considerada como uma arte. Embora ajam tecnologias para inibir a
possibilidade de as coisas acabarem mal, mesmo havendo métodos e filosofias para fornecerem um senso de direção, ainda
assim, muito estará sob a responsabilidade do desenvolvedor.
Não há um meio único meio seguro e consistente para desenvolver aplicações profissionais. O Desenvolvedor tem a tarefa
de pensar no que ainda não foi pensado. O trabalho dele não é apenas juntar partes, mas em muito desenvolver o que ainda
não foi elaborado.
Para que as coisas façam sentido em meio a dezenas de milhares de linhas de código, disciplina é necessário. Porém, nem
sempre disciplina é suficiente. Informações específicas são indispensáveis na maior parte dos casos.
Porque Documentar Algo que Profissionais São Capazes de Entender
por si Mesmos
Alguns desenvolvedores não vêem razão para se documentar o que está sendo desenvolvido, já que o que desenvolvemos
acaba sendo alterado de tempos em tempos, tornando obsoleto tudo que foi documentado. Alguns raciocinam que, uma
vez que bons Desenvolvedores devem entender bem o código com que trabalham, não é necessário explicar com outras
palavras o que está sendo expresso no código. Na verdade, alguns concluem que é até mesmo bom que um novo
programador tente entender cada linha de código para não deixar escapar nenhum detalhe.
Embora tais argumentos possam parecer sólidos, na prática há muito mais envolvido. As principais razões pelas quais esses
argumentos não são fundamentados é que nem sempre um bom desenvolvedor tem tempo suficiente para entender cada
linha do fonte; nem sempre esse desenvolvedor será capaz de entender todas as nuances do código e nem sempre ele irá de
fato entender o código. Talvez nem seja mesmo um “bom” desenvolvedor que lerá o seu código.
Assim, o mais sensato a si fazer é desenvolver reduzindo a margem de risco para si e para os seus companheiros de equipe.
Documentar não significa dizer que seus colegas não são bons profissionais, ou que eles são acomodados. Pelo contrário,
documentar bem indicará que você é um bom programador que não está acomodado.
Como Documentar
Embora seja um trabalho penoso, comentários são necessários para manter o fonte legível. Nós iremos agora passar as
regras para se documentar por meio de comentários, mas lembre-se: mesmo sendo muito importante fazer comentários, um
bom código se auto-documenta. Atribuir nomes que revelem algum significado às variáveis é bem melhor do que usar nomes
obscuros que você precisa explicar com comentários.
Ao escrever comentários, escreva para a sua audiência: o próximo desenvolvedor que precisará entender o seu fonte. Seja
generoso – o próximo pode ser você!
Estilo de Comentário Ao fazer comentários você usará as // (barras-duplas) do VDF. As linhas que trazem um comentário explicativo não devem
passar da coluna 100 do arquivo, que você pode estar verificando durante a digitação na parte inferior do Studio. Desse
modo, não será necessário que o programador use o scroll para ler comentários longos.
Também, mantenha os comentários consistentemente alinhados com o fonte. Assim, se você tiver documentando uma
linha de código que está começando, por exemplo, na coluna 4, coloque as // na coluna 4 também.
Após as //, separe o seu comentário com um espaço, como o exemplo de comentários a seguir.
18 Fascículos do Desenvolvedor
// Verifica se o usuário selecionou alguma mesa
Get LerRegistro of (oRecursosGlobais(Self)) REG_PDVRLOGIN_SELECT (&sValor) to iResult
If (iResult = OK_) Begin
Get RemoverRegistro of (oRecursosGlobais(Self)) REG_PDVRLOGIN_SELECT to iResult
// PDVRLOGIN_RESULT_MESA diz que a o resultado de PDVRLoginGarcom é uma mesa.
Clear CADMSA
Comentários do Arquivo Todo arquivo fonte deve ser iniciado com um comentário padrão com uma declaração de licença. Juntamente nesse bloco,
haverá um campo Desenvolvedor onde se informará o programador que criou o arquivo. Na linha seguinte haverá um
campo Colaborado Por onde será informado outros programadores que já trabalharam no mesmo código.
Caso você acabe tendo que fazer uma reestruturação completa do arquivo de um modo que produza grande impacto em
todo o arquivo, é apropriado que nessas condições você se intitule como Desenvolvedor do fonte e passe o
Desenvolvedor anterior para o campo Colaborado Por. Isso será muito útil na busca de informação de como o fonte
funciona.
O modelo a seguir demonstra isso.
// Copyright (c) 2011 Atual Sistemas. Todos os direitos reservados.
// O uso desse código-fonte é governado por leis concencente a software
// propietário e só é permitida sob autorização.
// Desenvolvedor: Claudio M. Souza Junior.
// Colaborado por: Rodrigo de Nadai Grigoleto
Arquivos PKG devem comentar uma visão geral do motivo pelo qual foram criados, e detalhar cada classe e função no seu
conteúdo. Arquivos SRC devem documentar qualquer coisa que fuja a regra no arquivo SRC do projeto. Já arquivos VW, DG
e SL deverão conter comentários apenas em relações as suas funções, procedimentos, propriedades e situações que podem
ser enganosas e sirvam como armadilhas. Não é necessário documenta botões, controles e coisas do tipo que são obvias ao
se avaliar o fonte. Também não é necessário documentar o cabeçalho de métodos sobrecarregados da framework do VDF
explicando esses procedimentos (ex activate). Apenas documente o que não for óbvio sobre a framework.
Comentários de Classes Toda a classe deve ter um comentário que explique seu propósito e dê exemplo do seu uso. Nunca separe o comentário da
verdadeira declaração da classe, por exemplo, por saltar uma linha, a fim de ser possível rastrear comentários de uma classe
específica.
// Classe cSlotTabelaDinamica
//
// O propósito dessa classe é criar uma tabela temporária em algum slot liver do Filelits a partir
// de uma tabela já existente, organizar o nome dos campos e permitir consulta aos valores dos
// registros correntes. Tudo isso de um modo que evite que o usuário tenha que criar uma série de
// variáveis para armazenar o valor de colunas desejadas, e diversas linhas de dados e variáveis
// para lidar com os dados ao utilizar Set_Field_Value.
//
// Essa classe é dependente da existência de um singleton da classe cRecursosGlobais chamado
// oRecursosGlobais.
//
// Exemplos:
//
// - Criando e consultando uma tabela dinâmica.
//
// A fim de poder se criar uma cópia da tabela em um slot livre do Filelist para se realizar
// consultas sem ter que se preocupar com numeração de campo e com os processos relacionado
// você pode fazer como abaixo.
//
// ------------------------------------------------------------------ CODIGO
// Handle hcstdEstImo
//
// Get Create U_cSlotTabelaDinamica to hcstdEstImo
//
// Send CriarTabelaTemporaria to hcstdEstImo "EstImo"
//
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
19 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
// Clear (phTabela(hcstdEstImo))
//
// Set_Field_Value (phTabela(hcstdEstImo)) (Campo(hcstdEstImo, "FILIAL")) to iFilial
// Set_Field_Value (phTabela(hcstdEstImo)) (Campo(hcstdEstImo, "PEDIDO")) to iPedido
// Set_Field_Value (phTabela(hcstdEstImo)) (Campo(hcstdEstImo, "SERIE" )) to sSerie
//
// Vfind (phTabela(hcstdEstImo)) 1 GT
// // Faz processos de busca...
// Move ((CampoValorCorrente(hcstdEstImo, "QUANTIDADE")) + nQuantidadeLancada) to ;
// nQuantidadeLancada
// // Continua processos de busca e finaliza o loop.
//
// Send Destroy to hcstdEstImo
// ------------------------------------------------------------------ CODIGO
//
Class cSlotTabelaDinamica is a cObject
Documente também outros fatores que precisam estar sincronizados para que a classe funcione de modo apropriado após a
descrição global e antes do exemplo prático. No exemplo acima é dado a alerta da necessidade de um singleton (objeto que
só pode uma única instância do seu tipo por toda a aplicação).
Portanto, ao documentar classes, siga a ordem.
Informações Gerais
Informações importantes para sincronia de processos.
Exemplos.
A seção de exemplos deve começar com o dizer Exemplo:. Tenha os exemplos de código separados por uma linha de traços
que vá até a coluna 75 seguido pela palavra CODIGO.
Não documente nessa parte do cabeçalho da classe informações detalhadas sobre métodos ou dados que não sejam
necessários para esclarecer o cabeçalho, pois essas informações devem ser documentadas no corpo da classe.
Comentário de Funções e Procedimentos Comentários de funções e procedures devem dar a definição do método comentado, explicando propósito, os parâmetros
presentes, e o valor de retorno quando há. Toda função e procedure (com exceção de métodos óbvios da framework do
VDF) devem ser precedidas por um comentário. Esse comentário deverá ser descritivo (“Abre um arquivo”) ao invés de
impessoal (“Abrir um arquivo”) ou imperativo (“Abra um arquivo”); o comentário deve descrever a função, ao invés de dizer o
que ela deverá fazer.
Esses comentários no cabeçalho da função não explicam como a função realiza sua tarefa, mas sim qual tarefa ela realiza.
Comentários que expliquem como as coisas são realizadas devem ser colocados dentro da função precedendo as linhas que
realizem as tarefas que eles explicam.
As informações que você pode colocar nos comentários na declaração da função são:
Qual é a entrada e a saída.
Para métodos de classes: se o objeto usará referências passadas pelos argumentos que não se originam da
chamada desse método e se ele irá liberar essa referência ou não.
Se a função aloca memória ou recursos que devem ser liberados por quem a chamou.
Se os argumentos são opcionais ou podem receber valores NULL.
Se o método em questão afeta a performance da aplicação.
Para função reentrante: O que deve ser levado em conta para sincronização?
No entanto, não use de palavras demais declarando o óbvio. Todas essas informações serão posta em um bloco divido em
três seções:
Instruções Gerais
Entrada
Saída
Abaixo segue um modelo.
20 Fascículos do Desenvolvedor
// Function PedirItemServidor String sTopico Returns Integer
//
// Essa função faz o pedido de um item ao servidor DDE por meio WM_DDE_REQUEST. Essa mensagem
// é postada para o servidor e se espera que a resposta seja postada também. O que indica que a
// resposta não é imediata.
//
// Entrada:
// - sTopico: O item requisitado ao servidor.
//
// Retorno: Em caso de sucesso, o valor de retorno é diferente de NULL.
//
Function PedirItemServidor String sTopico Return Integer cObject
Lembre-se sempre de separar a seção de entrada com o sinalizado escrito Entrada: e em seguida separe cada parâmetro
com um traço (-). Por fim, sempre use Retorno: para indicar a saída.
Ao comentar construtores e destrutores, certifique-se se realmente é necessário. Desenvolvedores em geral sabem qual é o
propósito de um construtor e de um destrutor. Portanto, comentários como “Destrói o objeto” não são úteis. Geralmente
esses métodos nem possuem comentários na declaração.
Comentários na definição dos métodos
Todo método deve ter comentários na sua definição se houver qualquer coisa enganosa na maneira que esse método realiza
o seu trabalho. Por exemplo, nos comentários de definição você pode citar as armadilhas da função, especificar os passos
que são percorridos ou explicar porque você optou por determinado caminho quando havia uma alternativa viável.
Não repita os comentários que estão na declaração da função, na definição do arquivo ou em outra parte. Não tem
problema recapitular o que a fonte faz, mas mantenha o foco em como ela faz isso.
Comentários de Propriedades e Variáveis Globais De um modo geral, nomes de variáveis e propriedades devem ser descritivos o bastante para se deduzir sua funcionalidade.
Porém, em alguns casos são necessários comentários.
Dados membros de classes
Toda propriedade membro de uma classe deve ter um comentário descrevendo seu propósito. Se a propriedade lida de
modo diferente com determinados valores como NULL e INVALID_HANDLE_VALUE, documente.
{ Visibility=Private}
// Essa propriedade indica se as operações de busca, exclusão ou inclusão de itens devem
// afetar visialmente o frame ou não. Toda operação em que o foco se move consecutivamente
// entre várias mesas deve usar esse recurso como falso.
Property Boolean pbSincronizado False
Note que esse comentário é mais longo do que o normal; ele trás referências ao uso dessa propriedade.
Variáveis Globais
Toda variável global deve ter um comentário descrevendo o que ela é e para que serve.
Comentários Complementares Ao fazer implementações, inserções ou derivações no fonte, comente as partes que sejam enganosas, obscuras,
interessantes ou importantes.
Situações complexas
Códigos que são enganosos ou complexos devem ser precedidos por comentários como no exemplo a seguir.
// Copia os valores de phSetItens na ordem em que piIndice está no set hArrayItens,
// fazendo com que os itens de phSetItens fiquem ordenados dentro de haItens e remove
// simultaneamente os valores de phSetItens.
While ((Item_Count(phSetItens(Self))) > 0)
Set Value of haItens item (Find_Element(hArrayItens,(piIndice(Value(phSetItens(Self), 0))))) ;
to (Value(phSetItens(Self), 0))
Send Delete_Item of (phSetItens(Self)) 0
Loop
// Então grava os valores haItens em phSetItens na ordem correta.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
21 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
While ((Item_Count(haItens)) > 0)
Send Add_Element of (phSetItens(Self)) (Value(haItens, 0))
Send Delete_Item of haItens 0
Loop
Situações Obscuras
Linhas que não são óbvias no seu propósito devem ter comentários descritivos. Esses comentários geralmente vem no fim
da linha, separados do fonte por dois espaços. Exemplo:
Handle hArrayItens // Set para armazenar todas as mesas que tem de ser exibidas.
Handle hItem // Handle para inserir uma nova mesa ou atualizar os valores de alguma.
Handle hValor // Handle usado para armazenar o Array de Confronto da mesa.
Handle hItemAtivo
Integer iItemInicial // Inteiro para identificar em ordem de posição de onde deve-se reaorganizar
// os itens visualmente. O valor mais baixo deve prevalecer nesse caso.
Integer i
// Guarda o handle do item ativo para ativá-lo novamente após o processo de atualização.
Move (phItemAtivo(Self)) To hItemAtivo
Note que ambos os comentários informam o que está acontecendo. Porém, os comentários de fim de linha estão dando
detalhes que não são óbvios a primeira vista. Esses comentários geralmente são pequenas dicas do fonte.
Também, como se pode notar no primeiro bloco, quando há vários comentários em linhas subseqüentes, é bom alinhá-los,
mesmo sendo comentários de fim de linha.
NULL, true/false, 1, 2, 3...
Quando você passa valores literais, booleanos ou NULL, você deve considerar a possibilidade de usar um comentário, ou
então tornar seu código auto-documentado por usar constantes. Compare o seguinte código:
Move (SendMessageTimeout(-1, WM_DDE_INITIATE, (Window_Handle(Self)), dwParam, ;
SMTO_ABORTIFHUNG, 10000, NULL)) to iResult
// O que esses valores significam????
Vesus:
Move (SendMessageTimeout(-1, ; // Envia para o Broadcast.
WM_DDE_INITIATE,;
(Window_Handle(Self)),;
dwParam, ;
SMTO_ABORTIFHUNG,; // Espera por um tempo e depois aborta.
10000, ; // 10segs em milésimos de segundo.
NULL)) ; // Sem ponteiro para valor de retorno.
to iResult
Caso você não queira usar comentários, então use pelo menos uma constante para identificar o valor mais facilmente.
Nunca descreva o código
Você não deve nunca descrever o código. Sempre pense que a pessoa lendo o seu código entende VDF e não é preciso
explicar como a linguagem funciona em si.
// Agora vá ao array B e assegure-se que se i aparecer
// o próximo elemento terá o valor de i+1.
// Que comentário inútil
Nunca comente coisas desse tipo.
Pontuação, Ortografia e Gramática Dê atenção a pontuação, ortografia e a gramática; é mais fácil ler comentários que são bem escritos.
22 Fascículos do Desenvolvedor
Comentários devem normalmente ser escritos como sentenças comuns com todos os aspectos da língua portuguesa sendo
respeitados. Comentários de fim de linha podem ser menos formais, mas você precisa ser consistente no estilo. Sentenças
completas são mais fáceis de entender e não passam a impressão de um pensamento mal terminado.
Talvez você não concorde a principio pelo fato de o revisor não aprovar seu código por causa de uma virgula que deveria ser
um ponto, mas é muito importante que o fonte seja mantido em um alto nível de clareza e legibilidade. Uma escrita correta
faz parte desse objetivo.
Comentários HACK e TODO Use comentário TODO para situações onde código precisa ser acrescido, onde se carece de uma solução ou funcionalidade.
Por outro lado, use comentários HACK onde o código deve ser temporário, foi usada uma solução para curto prazo ou
algum método que funciona, mas não perfeitamente (gambiarra).
TODO e HACK devem ser escritos com todas as letras maiúsculas, seguidos pelo nome da pessoa que pode melhor prover
um contexto a respeito do problema citado. Esses marcações no fonte não significam um compromisso de que a pessoa que
marcou deverá fazer a correção, por isso que o nome é necessário para que haja esclarecimentos necessários. Sempre
escreve tais palavras chaves no inicio da linha do comentário para facilitar as buscas.
// HACK(ClaudioJr): Os métodos usados nesse arquivo estão desatualizados.
// TODO(Wanderson): Incluir um novo método para busca de registro automático.
Se o seu TODO ou HACK incluir alguma previsão, inclua uma data específica (“Acertar em Novembro 2012”) ou um evento
bem específico (“Remover esse código quando todos os clientes estiverem no PAF.”).
Comentários de Depreciação Você pode marcar um método ou dado como depreciado por escrever DEPRECIADO em letras maiúsculas. Esse comentário
pode ser colocado tanto antes da declaração da interface como na mesma linha, e indicará que aquela interface não deve
mais ser usada, mesmo embora outras partes do fonte a usem.
Após escrever DEPRECIADO, coloque seu nome entre parênteses.
No comentário de depreciação você deve colocar instruções específicas para que outros consigam conserta as partes em
que há chamadas para a interface depreciada.
Depreciar uma interface não fará nenhuma mágica em qualquer um dos pontos onde ela é usada. Se você deseja que outros
parem de usar a interface depreciada, você precisará concerta isso por si mesmo, ou então designar alguém para isso. Mas o
fato de uma interface ser marcada como depreciada serve como ordem para não se fazer mais uso futuro dela.
Assim, qualquer código novo não pode conter chamadas para interfaces depreciadas. Se você não entendeu a instruções
sobre o que fazer com a interface depreciada, procure a pessoa que a depreciou.
Escreva seu próprio futuro
Nós Queremos Te Ouvir
Nós apreciaremos qualquer feedback a respeito desse material. Sua opinião e suas sugestões são muito relevantes e pode
nos levar a construir conteúdos melhores. Sua participação será uma ajuda.
Envie seu feedback para [email protected].
Equipe Editora
Desenvolvido e Escrito Por Claudio M. Souza Junior
Revisado e Editado Por Wanderson Lúcio Bastos
Todo Desenvolvedor pode escolher entre cumprir tarefas ou ter
paixão pelo que faz, entre fazer o possível ou fazer o impossível, entre
escrever um código ou escrever seu próprio futuro.
Atualize suas habilidades como desenvolvedor por conhecer e aplicar
Recursos e Idéias providos pelo Desenvolvimento da Atual Sistemas.