classes, relatórios e comentários intruções e padrões iniciais · obviamente, abstração é...

25
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

Upload: vothien

Post on 24-Jan-2019

216 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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

Page 2: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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

Page 3: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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

Page 4: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.

Page 5: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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.

Page 6: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.

Page 7: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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

Page 8: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.

Page 9: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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

Page 10: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.

Page 11: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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.

Page 12: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.

Page 13: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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).

Page 14: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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

Page 15: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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.

Page 16: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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

Page 17: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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

Page 18: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.

Page 19: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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.

Page 20: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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"

//

Page 21: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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.

Page 22: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.

Page 23: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

© 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.

Page 24: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.

Page 25: Classes, Relatórios e Comentários Intruções e Padrões Iniciais · Obviamente, abstração é um conceito, e não um recurso de linguagem. Assim, sempre que você tiver que criar

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.