ACCESS 2010_Classes.pdf
-
Upload
lucio-mathias -
Category
Documents
-
view
126 -
download
3
Transcript of ACCESS 2010_Classes.pdf
1
Classes - Introdução
Esta série de artigos tem o objetivo de apresentar o uso de
classes e disseminar a utilização da orientação a objetos
dentro de sistemas desenvolvidos em Access / Visual
Basic for Applications.
A programação orientada a objetos vem se difundindo
com grande vulto no mundo todo, resolvendo problemas
e causando revolução no desenvolvimento de sistemas.
Ganho de produtividade e qualidade nos produtos são apenas algumas das principais
características deste novo paradigma.
Logo não seria muito inteligente da nossa parte, amantes do Access/VBA, nos
mantermos distantes desta onda de tecnologia apenas por desconhecimento da
ferramenta.
Para que todos possam tirar proveito da OO (orientação a objetos) foi que nasceu a idéia
de ensinar e demonstrar como criar programas com a utilização de classes no Visual
Basic for Applications, a linguagem fornecida pelo MS-Access.
O trabalho não será dirigido para uma versão específica do Access, mas será
direcionado a todas as versões existentes, através da apresentação de conceitos e
ferramentas genéricos, para que todos tenham condições de se aproveitarem dos
conhecimentos aqui transmitidos.
Não serão descritos nem implementados os padrões de projetos que são padrões de fato,
amplamente utilizados em linguagens específicas e voltadas à POO, como Java e PHP,
por exemplo. Ao contrário, será utilizado um padrão próprio para fins didáticos,
também direcionados para a utilização da ferramenta case Genesis, que será tema de um
dos artigos.
Ao final dos artigos teremos um exemplo prático, pronto para utilização e visualização
da metodologia empregada. O exemplo será um banco de dados com um pequeno
sistema de vendas feito em Access com as funções totalmente implementadas utilizando
classes e objetos.
Além do objetivo principal dos artigos também será apresentada a utilização de uma
ferramenta case, o Genesis, um sotware capaz de auxiliar na construção de um banco de
dados funcional, desde a sua documentação até a criação das classes que serão utilizadas
pelo sistema.
O trabalho contará com 10 artigos, assim divididos:
I - ORIENTAÇÃO A OBJETOS: Histórico dos paradigmas de linguagens de
programação e apresentação de conceitos relativos à programação orientada a objetos;
II – PROGRAMAÇÃO OO NO ACCESS/VBA: Apresentação dos recursos de
2
orientação a objetos presentes no Visual Basic for Applications, com a forma de
utilização e as restrições existentes;
III – MODELAGEM DO SISTEMA DE VENDAS: Definição e modelagem de um
pequeno sistema de vendas de alimentos, bastantes simples, com o desenho das classes
e suas ligações para posterior implementação;
IV – AS CLASSES AUXILIARES: Implementação das classes auxiliares, as quais
contém funções que serão necessárias para o bom funcionamento das classes principais;
V – A CLASSE CLIENTE: Definidos os itens da classe será o momento da
implementação da classe Cliente, com a codificação de todos os atributos e métodos
necessários, além da interface gráfica para manipulação dos dados;
VI – A CLASSE PRODUTO: Implementação dos atributos e métodos da classe
Produto, além da interface gráfica para manipulação dos dados;
VII – AS CLASSES VENDA E DETALHE DE VENDA: Implementação dos atributos
e métodos da classe Venda e de sua classe de ligação DetalheVenda, que será
responsável pelo controle de quais produtos fazem parte de uma venda, além da
interface gráfica para manipulação dos dados;
VIII – FINALIZAÇÃO DO SISTEMA: Finalização do sistema de vendas, com a
criação do formulário principal e dos relatórios para apresentação dos dados;
IX – GENESIS: A FERRAMENTA CASE: Apresentação do Genesis, um aplicativo
capaz de agilizar a criação de um software em Access/VBA que utiliza classes em sua
estrutura, além de produzir elementos para a documentação do sistema.
X – CONCLUSÃO: Ao final concluiremos a série de artigos apresentando alguns
apanhados sobre o uso das classes nas diferentes versões do Access e as possibilidades
de utilização da orientação a objetos para melhorar o desempenho dos sistemas
desenvolvidos.
Vamos então dar início a esta jornada de programação imergindo no mundo dos objetos
e do nosso querido Access.
3
Classes II - Programação OO no Access/Vba
O Visual Basic for Applications oferece suporte a apenas alguns dos recursos da
orientação a objeto. Ele permite a criação de objetos personalizados com
encapsulamento, métodos construtores e destrutores, e, inclusive, interface de classe.
Entretanto não é possível implementar a herança, uma das características mais
importantes da orientação a objetos. Como conseqüência, também não é possível
utilizar o polimorfismo real com reimplementação de métodos. O polimorfismo neste
caso se restringe a permitir que diferentes classes possam ter o mesmo nome de método,
porém com objetivos e funcionamentos diferentes.
Como já vimos o VBA não fornece suporte ao recurso da herança, então qualquer
sistema que envolva classes deve ser projetado utilizando-se cada classe implementada
inteiramente. Mesmo que possua características semelhantes a outras, a utilização dos
mecanismos de generalização/especialização não poderá ser aplicada.
Outra característica marcante da metodologia de objetos, que é implementada na
maioria das linguagens a ela direcionadas, é a sobrecarga de métodos, em que uma
mesma classe possui dois ou mais métodos com o mesmo nome. Neste caso a classe
diferencia os métodos pelo tipo e quantidade de parâmetros passados como argumento
na realização da chamada. Infelizmente esta é outra funcionalidade que o VBA não
suporta. Cada método deve ter o seu nome, diferente de todos os outros dentro da
classe.
Contudo o objetivo deste artigo é apresentar e demonstrar como criar e utilizar uma
classe simples no VBA. Por isso mesmo não serão explanados tópicos aprofundados
sobre o assunto. Caso você se interesse em conhecer um pouco mais sobre a criação de
objetos pode consultar a própria ajuda do VBA, que possui definições, explicações e
exemplos sobre todos os assuntos aqui abordados.
Criando uma Classe no VBA
Para criar uma classe no VBA basta abrir o editor, através do menu do Access ou
utilizando a combinação de teclas Alt+F11. No editor escolhemos a opção Módulo de
classe no menu Inserir da janela do editor, conforme figura abaixo:
4
No momento em que salvarmos a classe o nome escolhido será o nome que utilizaremos
para criar objetos desta classe. A classe do exemplo foi salva com o nome clsCliente.
Diferença Entre os Tipos de Módulos
No VBA podemos ter os seguintes tipos de módulos: Módulo Padrão, Módulo de
Formulário/Relatório e Módulo de Classe.
A diferença básica entre estes tipos de módulos está na visibilidade das informações de
cada um. Em um módulo padrão, antes chamado de módulo de código, podemos
colocar constantes, variáveis, procedimentos e funções que são diretamente acessíveis
de qualquer lugar do nosso projeto, dependendo apenas dos modificadores de acesso
utilizados. Já em um módulo de classe não é possível acessar os atributos e métodos
sem que o objeto seja instanciado.
Resumindo nós podemos chamar uma função de um módulo padrão sem que seja
preciso primeiro criar uma variável de objeto para o módulo em questão. Para funções
dentro de um módulo de classe isto não é possível.
Já o módulo de formulário/relatório trata-se apenas de um módulo de classe que
pertence ao respectivo formulário/relatório, ou seja, contém os atributos e métodos
relativos ao formulário/relatório em questão.
Os módulos padrão e de classe devem ser explicitamente criados. Já os módulos de
formulário/relatório são criados automaticamente quando criamos um formulário ou
relatório.
5
Então resta decidir sobre quando utilizar um módulo padrão ou um módulo de
classe. Você tem ou já teve esta dúvida? A resposta está no fato de suas variáveis e
seus procedimentos/funções serem específicos ou genéricos. Caso possam ser usados
por qualquer entidade, em qualquer módulo, a qualquer momento seria bom que
estivessem em um módulo padrão para permitir o acesso direto e imediato. Um exemplo
deste caso seria uma função que converte um valor numérico em valor por extenso.
Com certeza esta é uma função bem genérica. Já em se tratando de algo específico, que
só tenha a ver com aquele determinado objeto, por exemplo um cálculo de estoque de
uma venda, deveria ser implementado dentro da classe que modela os objetos de venda,
para que fosse acessado somente durante a existência da mesma.
Os defensores mais radicais da POO diriam que mesmo para funções genéricas deveria
ser criada uma classe para agrupar aquelas que fossem semelhantes.
Criando os Atributos da Classe
Antes de criarmos os atributos vamos incluir algumas diretivas dentro no nosso módulo
de classe para restringir a ocorrência de erros e definir o padrão de comparação de
dados no sistema. Para isto utilizamos as seguintes opções:
> Para definir o método padrão de comparação de dados dentro do módulo de classe:
Option Compare Database
> Para obrigar a declaração de variáveis dentro do módulo de classe:
Option Explicit
Obs: Estas opções não são obrigatórias na criação das classes, mas é uma boa prática
adotá-las em seus módulos. A opção de comparação deve ser aquela mais adequada ao
tipo de comparação desejada. Para maiores informações sobre estas duas opções, além
de outras, consulte a documentação do VBA.
Agora para criarmos atributos dentro da nossa classe basta declararmos as variáveis ou
constantes, lembrando que no caso destas não poderemos atribuir valores.
Como iremos declarar as variáveis diretamente no módulo, sem que estejam
subordinadas a um procedimento ou função, não poderemos fazer esta declaração
usando a instrução Dim.
Neste caso deveremos utilizar os modificadores de acesso a seguir:
Private: para atributos privados, ou seja, aqueles que não são visíveis fora do módulo.
Normalmente utilizamos este modificador e configuramos o acesso aos atributos através
dos métodos de atribuição e retorno de valores que serão vistos a seguir;
Public: para atributos públicos, ou seja, aqueles que são visíveis fora do módulo.
Conforme citado anteriormente, é uma regra da POO que os atributos não sejam
diretamente acessíveis fora da classe. Isto é o encapsulamento das informações.
6
Vamos criar um atributo público em nossa classe para que possamos visualizar a sua
utilização:
‘Diretivas iniciais
Option Compare Database
Option Explicit
‘Primeiro atributo: nome do cliente
Public nomeCliente As String
Salvamos a classe e agora vamos criar um módulo padrão para testar a nossa classe
criada com seu primeiro e único atributo público.
Crie um módulo padrão, salve-o com o nome de TesteClasse e inclua este código em
um procedimento chamado Teste():
{geshi lang="asp" lines="true" tit="Testando o Objeto"}Option Compare Database
Option Explicit
Sub Teste()
'Declarando o objeto cliente
Dim objCliente As New clsCliente
'Atribuindo um valor ao atributo nomeCliente
objCliente.nomeCliente = "Plinio Mabesi"
'Exibindo o valor do atributo
MsgBox "Nome do Cliente: " & objCliente.nomeCliente
End Sub{/geshi}
Perceba que assim que digitamos o ponto após o nome do objeto o VBA nos mostra
quais atributos e métodos estão disponíveis, para que possamos escolher um deles na
lista suspensa, conforme a figura abaixo:
7
Após o procedimento estar completo posicione o cursor dentro dele e pressione F5 para
executar. Veja o resultado apresentado na caixa de mensagem. Faça novos testes
alterando o nome do cliente e reveja o resultado.
Atributos de Classe
O VBA não fornece o recurso presente em outras linguagens em que utilizamos o
modificador Static para criar o chamado atributo de classe, o qual não depende da
instanciação do objeto para ser acessado. Ele é um atributo que não está ligado a
nenhum objeto, ou seja, ele existe sem que seja necessário declarar uma variável de
objeto da classe.
No VBA a instrução Static tem outra função: a de reter um valor de uma variável dentro
de um procedimento ou função entre as diversas chamadas. Caso não se use esta
instrução o valor de uma variável é perdido assim que o procedimento/função for
encerrado. Com a instrução Static este valor estará disponível na próxima chamada,
desde que o aplicativo ainda esteja sendo executado.
Sendo assim todos os atributos e métodos de uma classe só estarão disponíveis depois
que o objeto for instanciado.
Métodos de Acesso aos Atributos
Conforme prevê as regras da POO, não se deve fornecer acesso direto aos atributos de
um objeto, obedecendo ao conceito de encapsulamento, pois no caso de qualquer
alteração no tipo de dado ou inclusão de uma ação a ser executada quando da atribuição
ou recuperação de um valor do atributo, a classe continuará oferecendo a mesma
interface de comunicação com outras classes. Isto resulta em esforço mínimo para a
manutenção ou extensão do código.
O VBA fornece um recurso nativo e próprio para a implementação de métodos que
atribuem ou retornam valores de atributos do objeto, os conhecidos getters e setters.
8
São os procedimentos Property Let (atribuem valores aos atributos, equivalente ao set
em outras linguagens) e Property Get (retornam valores de atributos, equivalente ao
get das outras linguagens).
Ambos os procedimentos devem ter o mesmo nome, mas ao instanciar um objeto o
usuário tem acesso aos métodos como se fossem uma única propriedade. Para atribuir
um valor basta igualar a propriedade ao valor de uma variável, por exemplo. Da mesma
forma, para recuperar o valor de um atributo para igualar uma variável à propriedade.
Vamos alterar nossa classe para deixar o atributo protegido utilizando os procedimentos
Property Get e Property Let. O código da classe agora ficará desta maneira:
Option Compare Database
Option Explicit
'Protegendo o atributo que ficará
'visível apenas dentro da classe
Private strNomeCliente As String
Property Get nomeCliente() As String
'Retornando o valor do atributo
nomeCliente = strNomeCliente
End Property
Property Let nomeCliente(argNomeCliente As String)
'Atribuindo valor ao atributo através
'do argumento passado para o método
strNomeCliente = argNomeCliente
End Property
Perceba que alteramos o nome da variável para strNomeCliente já que os métodos se
chamarão nomeCliente. Isto deve ser feito pois o VBA não aceita que o método tenha o
mesmo nome do atributo.
Ao digitarmos o nome do objeto em um módulo qualquer veremos a lista suspensa da
mesma maneira que vimos anteriormente, com apenas uma opção de escolha, porém
desta vez quem será acessado é um dos procedimentos Property (Get ou Let) que
dependerá da ação a ser executada, sendo uma atribuição ou um retorno.
Existe também outro mecanismo, diferente de outras linguagens, utilizado para
referência a objetos. Quando um objeto possui um atributo que faz referência a outro
objeto, a atribuição é feita através do procedimento Property Set e não através do
procedimento Property Let.
Para retornar o objeto referenciado utiliza-se também o procedimento Property Get,
porém ainda faz-se necessária a utilização da instrução Set por se tratar de um objeto.
'Declarando um atributo do tipo objeto
Private objAtributoObjeto As Object
Property Get nomeDoAtributo() As Object
9
'Retornando o objeto
Set nomeDoAtributo = objAtributoObjeto
End Property
Property Set nomeDoAtributo(argAtributo As Object)
'Atribuindo um objeto ao atributo
Set objAtributoObjeto = argAtributo
End Property
Executando Código ao Atribuir ou Retornar Valores dos Atributos
Uma das maiores vantagens em se utilizar métodos para atribuir ou retornar valores está
na capacidade de executar códigos imediatamente antes ou após a chamada ao atributo.
Suponhamos que o nome do cliente possa ser informado em letras minúsculas ou
maiúsculas, porém desejamos que ele seja armazenado somente em maiúsculas. Neste
caso incluímos um código que faz o ajuste antes de realmente atribuir o valor:
Property Let nomeCliente(argNomeCliente As String)
'Passando o nome do cliente para maiúscula
'antes de efetivamente atribuí-lo
strNomeCliente = UCase(argNomeCliente)
End Property
Assim podemos fazer qualquer validação ou ajuste necessário antes de repassar um
valor ao atributo.
Entretanto em outra ocasião poderíamos desejar que o nome do cliente pudesse ser
informado em letras minúsculas ou maiúsculas, e que ele também possa ser armazenado
desta maneira. Porém desejamos que ao retornar o valor ele seja convertido para
maiúsculas apenas para ser apresentado. Neste caso incluímos um código que faz o
ajuste no momento de retornar o valor do atributo:
Property Get nomeCliente() As String
'Passando o nome do cliente para maiúscula
'no momento de retorná-lo ao chamador
nomeCliente = UCase(strNomeCliente)
End Property
Criando os Métodos da Classe
Para criarmos métodos basta usarmos as instruções Sub ou Function, comuns aos
outros tipos de módulos, precedidos ou não dos modificadores de acesso para
visibilidade, e os mesmos formatos de declaração, com tipos de retorno, parâmetros e
regras de nomenclatura.
10
Observação: Mais uma vez volto a frisar que ensinar regras de criação de
procedimentos e funções não está no escopo deste trabalho, tampouco a construção de
algoritmos, então aos interessados cabe consultar os manuais da documentação do
VBA ou as milhares de apostilas existentes na Internet. Ou quem sabe em outro artigo...
Como é do conhecimento de todos utilizamos a instrução Sub para declarar
procedimentos, ou seja, códigos que ao final de sua execução não retornam nenhum
valor. Já a instrução Function serve para declararmos funções, ou seja, códigos que ao
final devem retornar algum resultado para o chamador.
Procedimentos e funções declaradas são públicas por padrão, a não ser que se indique
explicitamente o modificador de acesso. Para este caso, além de Private e Public
podemos ainda usar o modificador Friend, o qual ajusta a visibilidade do método para
ser acessível apenas dentro do projeto atual.
Como exemplo vamos criar dois métodos em nossa classe, um que não retorna nenhum
valor e servirá para exibir os dados do cliente em uma caixa de mensagem, e outro que
deverá retornar a idade do cliente com base na sua data de nascimento. Claro que para
isto deveremos incluir o atributo data de nascimento, concorda?
Então após incluir todos os códigos em nossa classe ela estará pronta e deverá ficar
assim:
Option Compare Database
Option Explicit
'Atributos===========================================
'Nome do Cliente
Private strNomeCliente As String
'Data de Nascimento do Cliente
Private dtmDataNascimento As Date
'Métodos Property Get / Let / Set ===================
Property Get nomeCliente() As String
nomeCliente = strNomeCliente
End Property
Property Let nomeCliente(argNomeCliente As String)
strNomeCliente = UCase(argNomeCliente)
End Property
Property Get dataNascimento() As Date
dataNascimento = dtmDataNascimento
End Property
Property Let dataNascimento(argDataNascimento As Date)
dtmDataNascimento = argDataNascimento
End Property
'Métodos============================================
11
Sub mostraDadosCliente()
'Método que mostra o nome do
'cliente em uma caixa de mensagem
MsgBox "Nome do Cliente: " & nomeCliente & vbCrLf & _
"Data de Nascimento: " & dataNascimento & vbCrLf & _
"Idade: " & calculaIdade, vbInformation, "Dados do
Cliente"
End Sub
Function calculaIdade() As Integer
'Método que calcula a idade com
'base na data de nascimento
Dim anoAtual As Integer
Dim anoNascimento As Integer
Dim totalAnos As Integer
Dim aniversario As String
aniversario = Format(dataNascimento, "dd/mm")
anoNascimento = Year(dataNascimento)
anoAtual = Year(Date)
totalAnos = anoAtual - anoNascimento
If CDate(aniversario & "/" & anoAtual) <= Date Then
calculaIdade = totalAnos
Else
calculaIdade = totalAnos - 1
End If
End Function
Perceba que ao chamarmos o método calculaIdade() no método mostraDadosCliente()
fazemos a chamada diretamente. Isto acontece porque os dois métodos estão dentro da
mesma classe. Qualquer chamada de fora da classe somente será possível depois da
declaração da variável de objeto da classe, ou seja, depois que o objeto for instanciado.
Retornando ao nosso módulo de teste podemos observar agora que temos mais opções
na lista suspensa ao digitar o ponto após o nome do objeto:
12
Vamos então atribuir valores às propriedades e testar os novos métodos do objeto a
partir do procedimento Teste() em nosso módulo TesteClasse:
Option Compare Database
Option Explicit
Sub Teste()
'Declarando o objeto cliente
Dim objCliente As New clsCliente
'Atribuindo um valor ao atributo nomeCliente
objCliente.nomeCliente = "Plinio Mabesi"
'Atribuindo um valor ao atributo dataNascimento
objCliente.dataNascimento = "16/01/1976"
'Exibindo a idade do cliente em uma caixa de mensagem
MsgBox "Idade do cliente: " & objCliente.calculaIdade
'Exibindo todos os dados do cliente, mas agora
'utilizando o método próprio criado para isto
objCliente.mostraDadosCliente
End Sub
Você poderá executar o código inteiro posicionando o cursor dentro do procedimento
com a tecla F5 ou então passo a passo usando a tecla F8, podendo inclusive acompanhar
a atribuição de cada um dos valores aos atributos.
Trabalhando com Vários Objetos
Outro aspecto interessante de se trabalhar com objetos é que a partir de um único
módulo podemos criar quantos objetos forem necessários, e o que é melhor, eles podem
13
interagir e coexistir simultaneamente. Para isto basta declararmos cada objeto com um
nome diferente.
Vamos a um exemplo prático em nosso módulo de testes, no qual dois objetos cliente
terão as suas idades comparadas para saber quem tem mais idade, sem considerar meses
e dias:
Sub TesteIdade()
'Declarando os objetos cliente
Dim objCliente01 As New clsCliente
Dim objCliente02 As New clsCliente
'Atribuindo valores aos objetos
objCliente01.nomeCliente = "Plinio Mabesi"
objCliente01.dataNascimento = "16/01/1976"
objCliente02.nomeCliente = "Avelino Sampaio"
objCliente02.dataNascimento = "20/08/1965"
'Realizando a comparação entre os dois objetos
If objCliente01.calculaIdade = objCliente02.calculaIdade Then
MsgBox objCliente01.nomeCliente & " e " &
objCliente02.nomeCliente
& " tem a mesma idade."
ElseIf objCliente01.calculaIdade < objCliente02.calculaIdade Then
MsgBox objCliente01.nomeCliente & " é mais novo que "
& objCliente02.nomeCliente & "."
Else
MsgBox objCliente02.nomeCliente & " é mais novo que "
& objCliente01.nomeCliente & "."
End If
End Sub
Execute e veja o resultado. Altere os valores e analise o comportamento e o resultado
das variações. Tente inventar outros testes, inclua novos atributos na classe, assim como
novos métodos, use a imaginação, treine, treine e treine, pois só assim você começará a
dominar a técnica da programação orientada a objetos. Você não vai querer continuar
fora desta, vai?
Criando uma Interface de Classe
Há situações em que é interessante que algumas classes implementem certos métodos
que são padrão para todas elas. Neste caso podemos criar uma interface, que nada mais
é do que uma classe que possui métodos não implementados, que deverão ser
construídos na classe que está utilizando a interface. Por isso ela serve apenas como um
contrato de implementação, o que significa que a classe deverá obrigatoriamente possuir
aqueles métodos com os mesmos parâmetros de entrada e o mesmo tipo de dado de
14
retorno. Mas a classe não precisa possuir somente os métodos da interface, ela poderá
ter quantos métodos forem necessários, desde que implemente os obrigatórios.
Diferentemente de outras linguagens, no VBA uma classe totalmente implementada
pode ser interface de outras, bastando para isso incluir a instrução Implements dentro
da classe que deseja implementar a interface de outra.
Como exemplo se quiséssemos criar outra classe em nosso pequeno projeto, agora para
fornecedores, mas que obrigatoriamente tivesse que implementar todos os métodos da
nossa classe de clientes deveríamos incluir este código dentro do módulo da classe
clsFornecedor, logo no início:
Implements clsCliente
Quando inserimos a instrução Implements em uma classe o menu dropdown no editor
de código, logo abaixo da barra de ferramentas nos apresenta as opções disponíveis,
bastando selecioná-las para que sejam incluídas na classe atual. Veja a figura:
Os métodos implementados desta maneira deverão conter o nome da interface utilizada
antes do nome do método. Assim nossa classe clsFornecedor ficaria com o seguinte
aspecto, com os métodos ainda a serem implementados:
Option Compare Database
Option Explicit
Implements clsCliente
Private Property Get clsCliente_dataNascimento() As Date
End Property
Private Property Let clsCliente_dataNascimento(RHS As Date)
End Property
Private Property Let clsCliente_nomeCliente(RHS As String)
15
End Property
Private Property Get clsCliente_nomeCliente() As String
End Property
Private Sub clsCliente_mostraDadosCliente()
End Sub
Private Function clsCliente_calculaIdade() As Integer
End Function
É lógico e evidente que este é apenas um exemplo utilizando uma classe já pronta, e que
não serviria em uma situação real pois os nomes dos métodos foram criados para um
cliente e não para um fornecedor.
Na verdade deveríamos criar uma interface com nomes de métodos genéricos que
pudessem servir para as classes de modo geral. O método mostraDadosCliente()
poderia se chamar apenas mostraDados(). Já o método calculaIdade() está em um
padrão aceitável e poderia ser utilizado para qualquer classe que se tratasse de uma
pessoa.
Note que o próprio VBA modifica os nomes dos parâmetros de métodos que os exigem.
Porém isto não é obrigatório e você pode alterar novamente estes argumentos.
Conclusão
Aqui se encerra mais esta etapa da nossa série. Neste artigo aprendemos as técnicas
básicas para se criar uma classe no VBA, criar e proteger seus atributos, além de como
criar e utilizar os métodos da classe para oferecer funcionalidade.
Vimos também como trabalhar com mais de um objeto da classe ao mesmo tempo,
assim como tivemos uma noção sobre implementação de interfaces. Com isso
percorremos mais um trecho na caminhada rumo ao conhecimento do paradigma
orientado a objetos.
16
Classes III - Modelagem do Sistema de Vendas
Além disso o modelo adotado tem caráter didático e não pretende servir como um
padrão para aplicações reais. As relações criadas, as funções definidas e os atributos
escolhidos terão como objetivo maior demonstrar como funciona a aplicação da
orientação a objetos no Access / VBA, mesmo não sendo a forma mais correta e
eficiente de modelagem de classes ou de banco de dados.
Portanto preocupe-se em visualizar e compreender bem as técnicas utilizadas nas
chamadas de métodos, criação de atributos, passagem de parâmetros, enfim esteja
focado em aprender como se dá a interação entre os objetos dentro do sistema.
Padrão de Nomenclatura
O padrão que será utilizado neste trabalho obedecerá às seguintes definições:
> Não serão usados acentos, símbolos ou espaços nos nomes, já que isto é uma prática
que causa problemas na maioria das linguagens de programação existentes, não sendo
nem mesmo aceita em várias delas.
> Nomes de classes começando em maiúsculas, com o restante em minúsculas. Quando
o nome da classe for formado por mais de uma palavra elas serão separadas por
maiúscula.
Ex: Cliente, Venda, DetalheVenda;
> Nomes de atributos em minúsculas. Quando o nome do atributo for formado por mais
de uma palavra elas serão separadas por maiúscula.
Ex: cpf, nomeCliente, descricao;
> Nomes de métodos em minúsculas seguidos por parênteses. Quando o nome do
atributo for formado por mais de uma palavra elas serão separadas por maiúscula.
Ex: obter(), salvar(), consulta(), calculaClasse();
> Nomes de tabelas começando em maiúsculas, com o restante em minúsculas. Quando
o nome da tabela for formado por mais de uma palavra elas serão separadas por
maiúscula.
Ex: Cliente, Venda, DetalheVenda;
> Nomes de campos de tabelas em minúsculas. Quando o nome do campo for formado
por mais de uma palavra elas serão separadas por maiúscula.
Ex: cpf, nomeCliente, descricao;
> Nomes de consultas começando em maiúsculas, precedidos pela letra C, com o
restante em minúsculas. Quando o nome da consulta for formado por mais de uma
palavra elas serão separadas por maiúscula.
17
Ex: CCliente, CVenda, CDetalheVenda;
> Nomes de formulários começando em maiúsculas, precedidos pela letra F, com o
restante em minúsculas. Quando o nome do formulário for formado por mais de uma
palavra elas serão separadas por maiúscula.
Ex: FCliente, FVenda, FDetalheVenda;
> Nomes de relatórios começando em maiúsculas, precedidos pela letra R, com o
restante em minúsculas. Quando o nome o relatório for formado por mais de uma
palavra elas serão separadas por maiúscula.
Ex: RCliente, RVenda, RDetalheVenda;
Obs: No momento da implementação das classes será adicionado o prefixo cls ao nome
daquelas que dizem respeito a objetos persistentes e acl ao nome das que são
auxiliares. O motivo desta escolha será explicado no artigo sobre o Genesis, a
ferramenta case para o Access.
Modelo de Classes
Para criarmos um sistema de vendas necessitaremos dos seguintes itens básicos:
1- Os PRODUTOS que serão vendidos;
2- Os CLIENTES que irão comprar os PRODUTOS;
3- Cada grupo de PRODUTOS vendido a um CLIENTE irá gerar uma VENDA.
Então inicialmente podemos dizer que nosso sistema deverá ser capaz de manipular os
objetos Cliente, Produto e Venda. Sendo assim estas serão as classes que deveremos
modelar.
Obs: Consideraremos que já foi feito um prévio levantamento de requisitos e que já
foram definidos quais atributos e métodos as classes devem ter.
Nas interações entre os objetos poderemos perceber que:
1- Um cliente pode ser cadastrado mesmo que não tenha comprado ainda;
2- Então um cliente cadastrado pode comprar zero ou mais vezes, por isso poderão ser
realizadas
zero ou mais vendas para cada cliente;
3- Cada venda realizada deve ser feita, obrigatoriamente, a um determinado cliente;
4- Cada produto cadastrado pode estar presente ou não em uma ou mais vendas;
5- Cada venda realizada deve conter, obrigatoriamente, um ou mais produtos.
18
A figura abaixo ilustra então o modelo de classes referente à descrição anterior:
Para executar funções genéricas e comuns a várias classes do sistema criaremos ainda
mais duas classes, uma para realizar a conexão com banco de dados e persistir ou buscar
os objetos e outra para realizar tarefas de validação e transformação de valores, as quais
serão utilizadas pela classe Cliente. Estas serão chamadas de ConexaoBD e Utilitario.
Mas antes de definirmos o esquema básico de nossas classes temos que ajustar a relação
entre Venda e Produto. Como todos já perceberam esta é uma relação muitos-para-
muitos, então as regras de modelagem nos mandam criar uma classe de associação
para gerenciar a ligação entre elas. Portanto teremos também a classe DetalheVenda.
Estas serão as outras classes criadas para completar nosso modelo:
Descrição das Classes
A seguir serão descritas as estruturas básicas de cada uma das classes. Porém neste
artigo ainda não serão detalhadas as formas de implementação das funcionalidades de
cada método definido no modelo. Apenas a assinatura de cada método será informada,
contendo seu objetivo, seus parâmetros de entrada e seu tipo de retorno. Os métodos
Let, Get e Set também não serão abordados por enquanto.
Classe Utilitário
Objetivo: oferecer funcionalidades de validação de dados e transformação de valores,
necessárias para o correto funcionamento da classe Cliente, ou qualquer outra classe ou
função que venha a precisar dela, a qualquer momento. Isto se deve ao fato da classe
possuir métodos genéricos e reutilizáveis, podendo ser reaproveitados, sem nenhuma
alteração, em qualquer sistema.
Funções:
validaCpf(argCpf As String) As Boolean: recebe como argumento um texto contendo
um CPF, sem pontos nem traço, e devolve um valor booleano, verdadeiro ou falso,
19
indicando se o CPF é ou não válido;
validaEmail(argEmail As String) As Boolean: recebe como argumento um texto
contendo um e-mail e devolve um valor booleano, verdadeiro ou falso, indicando se o e-
mail é ou não válido, sendo que a verificação é feita apenas no seu formato;
nomeProprio(argNome As String) As String: recebe como argumento um texto qualquer
e devolve o mesmo texto com as iniciais dos nomes em maiúsculas e o restante em
minúsculas, levando em consideração as partículas de ligação de nomes, as quais
permanecem em minúsculas;
desacentua(argTexto As String) As String: recebe como argumento um texto qualquer e
devolve o mesmo texto sem acentos;
abreviaNome(argNome As String) As String: recebe como argumento um texto
qualquer e devolve o mesmo texto com a penúltima parte do nome abreviada, levando
em consideração as partículas de ligação de nomes, as quais permanecem inalteradas.
Classe ConexaoBD
Objetivo: oferecer funcionalidades de consulta e atualização do banco de dados. Possui
também métodos genéricos que podem ser reaproveitados em qualquer sistema. Uma
das maiores vantagens de se utilizar uma classe de conexão é que no caso de mudança
do tipo de Sistema Gerenciador de Banco de Dados (SGBD) somente ela deverá ser
alterada, permanecendo todas as outras intocadas, desde que seja mantida a interface de
comunicação dos métodos. Um exemplo seria a troca do back-end para MySql, Postgres
ou Sql Server.
Funções:
executa(codigoSql As String) As Long: recebe como argumento um texto contendo um
código SQL de inserção, exclusão ou alteração, executa o código e devolve o número de
registros afetados na operação;
consulta(codigoSql As String, Optional editavel As Boolean = False) As Recordset:
recebe como argumento um texto contendo um código SQL de consulta de registros e
um valor lógico que define o modo de bloqueio de execução, permitindo ou não a
edição dos dados durante a operação, e devolve um recordset contendo o conjunto de
registros que atenderem aos critérios;
logicoSql(ByVal argValor As Boolean) As String: recebe como parâmetro um valor
booleano e devolve um texto contendo os valores True ou False, necessário para
códigos SQL;
pontoVirgula(varValor As Variant) As String: recebe como parâmetro um valor decimal
em que o padrão de separação da parte inteira e a decimal é a vírgula, e devolve um
texto contendo o mesmo valor agora separado por ponto, necessário para códigos SQL;
dataSql(ByVal argData As Date) As String: recebe como parâmetro uma data em
qualquer formato e devolve um texto contendo a data no padrão #mm/dd/yyyy#,
20
necessário para códigos SQL;
valorSql(ByVal argValor As Variant) As String: recebe como parâmetro um valor
qualquer, verifica o seu formato e devolve um valor no formato padrão dos códigos
SQL, fazendo uso das funções anteriores, caso necessário.
Classe Cliente
Objetivo: oferecer funcionalidades de consulta e atualização dos objetos do tipo cliente.
Atributos:
• codCliente (Long);
• cpf (String);
• nomeCliente (String);
• email (String);
• renda (Currency);
• classe (String).
Funções:
existe(argCodCliente As Long) As Boolean: recebe como argumento um código de um
cliente, utiliza a classe de conexão para consultar e devolve um valor booleano que
indica se o cliente existe ou não no banco de dados;
incluir() As Boolean: persiste o objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso.
Este é um método privado, somente sendo acessível pelos métodos internos da classe;
obter(argCodCliente As Long) As Boolean: recebe como argumento um código de um
cliente, utiliza a classe de conexão para consultar, atualiza o objeto com os dados e
devolve um valor booleano que indica se o cliente foi buscado com sucesso;
salvar() As Boolean: salva objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso.
Este método atualiza o objeto atual caso ele já exista ou então o inclui no banco de
dados caso seja um novo objeto;
excluir() As Boolean: exclui o objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso;
calculaClasse() As String: calcula a classe social do objeto atual baseado na renda
informada e devolve uma letra correspondente à sua classificação dentro das faixas
salariais.
Classe Produto
Objetivo: oferecer funcionalidades de consulta e atualização dos objetos do tipo
produto.
21
Atributos:
• codProduto (Long);
• unidade (String);
• descricao (String);
• valorUnitario (Currency);
• estoqueMinimo (Double);
• qtdEstoque (Double).
Funções:
existe(argCodProduto As Long) As Boolean: recebe como argumento um código de um
produto, utiliza a classe de conexão para consultar e devolve um valor booleano que
indica se o produto existe ou não no banco de dados;
incluir() As Boolean: persiste o objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso.
Este é um método privado, somente sendo acessível pelos métodos internos da classe;
obter(argCodProduto As Long) As Boolean: recebe como argumento um código de um
produto, utiliza a classe de conexão para consultar, atualiza o objeto com os dados e
devolve um valor booleano que indica se o produto foi buscado com sucesso;
salvar() As Boolean: salva objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso.
Este método atualiza o objeto atual caso ele já exista ou então o inclui no banco de
dados caso seja um novo objeto;
excluir() As Boolean: exclui o objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso;
baixarEstoque(argQtd As Double) As Boolean: atualiza o estoque diminuindo a
quantidade informada como parâmetro e devolve um valor booleano que indica o
sucesso da operação;
subirEstoque(argQtd As Double) As Boolean: atualiza o estoque acrescentando a
quantidade informada como parâmetro e devolve um valor booleano que indica o
sucesso da operação;
estoqueBaixo() As Boolean: verifica o estoque atual e compara com o valor de estoque
mínimo cadastrado para produto e devolve um valor booleano que indica se o valor
atual está abaixo do previsto.
Classe Venda
Objetivo: oferecer funcionalidades de consulta e atualização dos objetos do tipo venda.
Atributos:
22
• codVenda (Long);
• codCliente (Long);
• dataVenda (Date);
Funções:
existe(argCodVenda As Long) As Boolean: recebe como argumento um código de uma
venda, utiliza a classe de conexão para consultar e devolve um valor booleano que
indica se o venda existe ou não no banco de dados;
incluir() As Boolean: persiste o objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso.
Este é um método privado, somente sendo acessível pelos métodos internos da classe;
obter(argCodVenda As Long) As Boolean: recebe como argumento um código de uma
venda, utiliza a classe de conexão para consultar, atualiza o objeto com os dados e
devolve um valor booleano que indica se o venda foi buscada com sucesso;
salvar() As Boolean: salva objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso.
Este método atualiza o objeto atual caso ele já exista ou então o inclui no banco de
dados caso seja um novo objeto;
excluir() As Boolean: exclui o objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso;
getValorTotal() As Currency: Calcula o valor total da venda atual e devolve o valor
como resultado.
Classe DetalheVenda
Objetivo: oferecer funcionalidades de consulta e atualização dos objetos de ligação
entre a venda e os produtos componentes.
Atributos:
• codVenda (Long);
• codProduto (Long);
• qtdProduto (Double);
Funções:
existe(argCodVenda As Long, argCodProduto As Long) As Boolean: recebe como
argumento um código de uma venda e de um produto, utiliza a classe de conexão para
consultar e devolve um valor booleano que indica se objeto existe ou não no banco de
dados;
incluir() As Boolean: persiste o objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso.
Este é um método privado, somente sendo acessível pelos métodos internos da classe;
23
obter(argCodVenda As Long, argCodProduto As Long) As Boolean: recebe como
argumento um código de uma venda e de um produto, utiliza a classe de conexão para
consultar, atualiza o objeto com os dados e devolve um valor booleano que indica se o
objeto foi buscado com sucesso;
salvar() As Boolean: salva objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso.
Este método atualiza o objeto atual caso ele já exista ou então o inclui no banco de
dados caso seja um novo objeto;
excluir() As Boolean: exclui o objeto atual no banco de dados, utilizando a classe de
conexão, e devolve um valor booleano que indica se a operação ocorreu com sucesso;
getSubTotal() As Currency: Calcula o subtotal do produto vendido para a venda atual e
devolve o valor como resultado;
getListaProduto(argCodVenda As Long) As Recordset: recebe como parâmetro um
código de uma venda e devolve um conjunto de registros contendo os códigos dos
produtos relativos à venda informada.
Diagrama de Alto Nível
Organizando as relações entre todas as classes do sistema chegamos a este modelo de
alto nível, ainda sem informações de atributos e métodos, para melhor visualização do
contexto do sistema:
Diagrama de Classes Detalhado
Incluindo agora o restante das informações de atributos e métodos chegamos a este
diagrama detalhado:
24
A fim de evitar sobrecarregar o modelo com informações foram omitidos os métodos de
acesso aos atributos, os Get / Let / Set, que serão incluídos apenas na implementação
das classes, nos próximos artigos. Além disso atributos auxiliares também não foram
descritos nesta etapa, mas serão incluídos no momento da implementação das classes.
Modelo de Dados
Para que um objeto seja persistente, ou seja, possa ser recuperado posteriormente, ele
deverá ser gravado em algum meio permanente. Neste caso vamos persistir nossos
objetos em um gerenciador de banco de dados bastante conhecido de todos: o próprio
Access.
Realizando o mapeamento das classes para o formato de banco dados do Access temos
então o dicionário de dados, com as informações sobre cada tabela e seus campos, além
dos relacionamentos entre elas, necessários para manter a integridade dos dados. Temos
também o diagrama de entidade-relacionamento, que é a apresentação gráfica do
dicionário. Ressalto que do nosso DER foram suprimidas as informações detalhadas dos
campos para evitar sobrecarregar o modelo, já que este também não é o foco do presente
trabalho.
Dicionário de Dados
No dicionário de dados de um modelo de dados estão descritas as informações
detalhadas sobre as tabelas, seus campos, tipos de dados, tamanho dos campos, chaves
primárias e estrangeiras, além das respectivas referências estrangeiras.
Em nosso sistema teremos as seguintes tabelas mapeadas e seus campos:
Cliente
25
Armazena os dados cadastrais dos clientes.
Campo Descrição Tipo
codCliente PK - Código que identifica o cliente Long(campo
chave)
cpf CPF do cliente String(11)
nomeCliente Nome completo do cliente String(50)
email E-mail do cliente String(50)
renda Renda mensal do cliente, em Reais Currency
classe Classe social do cliente, calculada de acordo com
arenda String(1)
Produto
Armazena as informações sobre os produtos.
Campo Descrição Tipo
codProduto PK - Código que identifica o produto Long(campo chave)
descricao Descrição do produto String(30)
unidade Unidade de comercialização do produto String(5)
valorUnitario Valor unitário do produto, em Reais Currency
estoqueMinimo Quantidade mínima desejada no estoque Double
qtdEstoque Quantidade atual do estoque Double
Venda
Armazena informações sobre as vendas realizadas.
Campo Descrição Tipo
codVenda PK - Código que identifica a venda Long(campo chave)
codCliente FK - Código que indica o cliente relativo à venda Long
dataVenda Data de realização da venda Date/Time
DetalheVenda
Armazena informação de ligação entre a Venda e os seus Produtos integrantes.
Campo Descrição Tipo
26
codProduto PK - FK - Código que indica o produto
relacionado
Long(campo chave)
FK:
Produto(codPoduto)
codVenda PK - FK - Código que indica a venda relacionada Long(campo chave)
FK: Venda(codvenda)
qtdProduto Quantidade do produto relacionado Double
Obs: PK: Primary Key (chave primária) / FK: Foreign Key (chave estrangeira)
Diagrama de Entidade-Relacionamento
Para facilitar a visualização e permitir que o modelo de dados seja melhor
compreendido o dicionário de dados é convertido em um desenho que apresenta as
tabelas e seus campos de forma gráfica. Desta maneira a percepção das relações fica
bem mais simples.
Este é o Diagrama de Entidade-Relacionamento (DER) do nosso sistema de vendas:
Assim como a modelagem de classes utilizando a UML, a modelagem do diagrama de
entidade-relacionamento não será abordado em detalhes neste curso. Então mais uma
vez para se aprofundar no assunto procure material especializado em livros ou na
Internet, ou aguarde outro artigo em um futuro próximo.
Conclusão
Este artigo teve o objetivo de iniciar realmente a construção do sistema que será objeto
de estudo desta série, através da definição de quais serão as partes componentes do
projeto.
Apesar de não nos aprofundarmos no assunto foi possível termos uma noção de
utilização de padrões de nomenclatura e da modelagem com a UML, conhecendo o
diagrama de classes e percebendo a diferença entre os níveis de visualização do sistema
pelo detalhamento das informações nos modelos.
Além disso conhecemos um pouco sobre o mapeamento das classes para o modelo
relacional, utilizando o dicionário de dados e o diagrama de entidade-relacionamento
como ferramentas.
27
Mesmo não abrangendo todas as etapas do desenvolvimento de um software ficou clara
a importância de se planejar bem o que deverá ser construído, através da modelagem e
da descrição dos itens, tarefa esta que faz parte da fase de análise do sistema.
Bem, como tudo já está definido a nossa etapa seguinte será colocar a mão na massa,
iniciando pela codificação das classes auxiliares, que serão úteis para o funcionamento
das restantes.
28
Classes IV - As Classes Auxiliares
No último artigo vimos a modelagem do sistema. Agora chegou a hora de começarmos
a codificar nossas primeiras classes.
As classes auxiliares serão responsáveis por realizar tarefas genéricas que atendem a
solicitações de diversas outras classes ou mesmo outros módulos do sistema.
Poderíamos criar inúmeras classes auxiliares, com funções agrupadas pelo tipo de tarefa
a ser realizada, assim como a classe ConexaoBD que trata de questões de conexão com
banco de dados e a classe Utilitario que contempla funções de validação e conversão de
dados.
A idéia principal aqui é a reutilização de código, um dos pilares da orientação a
objetos. Percebam que nós codificaremos a classe ConexaoBD apenas uma única vez
em toda a nossa vida. No próximo sistema que montarmos simplesmente a
importaremos para o novo software, e a classe já deverá estar pronta para ser
usada. Pode ser que a codifiquemos para outro tipo de banco de dados, por exemplo
uma ConexaoBDMySql ou uma ConexaoBDSqlServer, mas cada codificação será única
e praticamente eterna.
Vamos então rever a descrição de cada classe. Em seguida faremos a codificação. O
código das funções será apresentado apenas com comentários mas sem detalhamento, já
que (novamente) a criação de algoritmos e a estruturação de procedimentos e funções
não é o foco deste curso.
A Classe Utilitario
Objetivo: oferecer funcionalidades de validação de dados e transformação de valores,
necessárias para o correto funcionamento da classe Cliente, ou qualquer outra classe ou
função que venha a precisar dela, a qualquer momento. Isto se deve ao fato da classe
possuir métodos genéricos e reutilizáveis, podendo ser reaproveitados, sem nenhuma
alteração, em qualquer sistema.
Método validaCpf(argCpf As String) As Boolean: recebe como argumento um texto
contendo um CPF, sem pontos nem traço, e devolve um valor booleano, verdadeiro ou
falso, indicando se o CPF é ou não válido;
Function validaCPF(argCpf As String) As Boolean
'Função que verifica a validade de um CPF.
Dim wSomaDosProdutos
Dim wResto
Dim wDigitChk1
Dim wDigitChk2
Dim wStatus
Dim wI
29
'Inicia o valor da Soma
wSomaDosProdutos = 0
'Para posição I de 1 até 9
For wI = 1 To 9
'Soma = Soma + (valor da posição dentro do CPF x (11 - posição))
wSomaDosProdutos = wSomaDosProdutos + Val(Mid(argCpf, wI, 1)) * (11 -
wI)
Next wI
'Resto = Soma - ((parte inteira da divisão da Soma por 11) x 11)
wResto = wSomaDosProdutos - Int(wSomaDosProdutos / 11) * 11
'Dígito verificador 1 = 0 (se Resto=0 ou 1 ) ou 11 - Resto (nos casos restantes)
wDigitChk1 = IIf(wResto = 0 Or wResto = 1, 0, 11 - wResto)
'Reinicia o valor da Soma
wSomaDosProdutos = 0
'Para posição I de 1 até 9
For wI = 1 To 9
'Soma = Soma + (valor da posição dentro do CPF x (12 - posição))
wSomaDosProdutos = wSomaDosProdutos + (Val(Mid(argCpf, wI, 1)) * (12 -
wI))
Next wI
'Soma = Soma (2 x dígito verificador 1)
wSomaDosProdutos = wSomaDosProdutos + (2 * wDigitChk1)
'Resto = Soma - ((parte inteira da divisão da Soma por 11) x 11)
wResto = wSomaDosProdutos - Int(wSomaDosProdutos / 11) * 11
'Dígito verificador 2 = 0 (se Resto=0 ou 1 ) ou 11 - Resto (nos casos restantes)
wDigitChk2 = IIf(wResto = 0 Or wResto = 1, 0, 11 - wResto)
'Se o dígito da posição 10 = Dígito verificador 1 E
'dígito da posição 11 = Dígito verificador 2 Então
If Mid(argCpf, 10, 1) = Mid(Trim(Str(wDigitChk1)), 1, 1) And _
Mid(argCpf, 11, 1) = Mid(Trim(Str(wDigitChk2)), 1, 1) Then
'CPF válido
validaCPF = True
Else
'CPF inválido
validaCPF = False
End If
End Function
Método validaEmail(argEmail As String) As Boolean: recebe como argumento um
texto contendo um e-mail e devolve um valor booleano, verdadeiro ou falso, indicando
se o e-mail é ou não válido, sendo que a verificação é feita apenas no seu formato;
Function validaEmail(eMail As String) As Boolean
'Função de validação do formato de um e-mail.
Dim posicaoA As Integer
Dim posicaoP As Integer
30
'Busca posição do caracter @
posicaoA = InStr(eMail, "@")
'Busca a posição do ponto a partir da posição
'do @ ou então da primeira posição
posicaoP = InStr(posicaoA Or 1, eMail, ".")
'Se a posição do @ for menor que 2 OU
'a posição do ponto for menor que a posição
'do caracter @
If posicaoA < 2 Or posicaoP < posicaoA Then
'Formato de e-mail inválido
validaEmail = False
Else
'Formato de e-mail válido
validaEmail = True
End If
End Function
Método nomeProprio(argNome As String) As String: recebe como argumento um texto
qualquer e devolve o mesmo texto com as inicias dos nomes em maiúsculas e o restante
em minúsculas, levando em consideração as partículas de ligação de nomes, as quais
permanecem em minúsculas;
Function nomeProprio(argNome As String) As String
'Função recursiva para converter a primeira letra
'dos nomes próprios para maiúscula, mantendo os
'aditivos em caixa baixa.
Dim sNome As String
Dim lEspaco As Long
Dim lTamanho As Long
'Pega o tamanho do nome
lTamanho = Len(argNome)
'Passa tudo para caixa baixa
argNome = LCase(argNome)
'Se o nome passado é vazio
'acaba a função ou a recursão
'retornando string vazia
If lTamanho = 0 Then
nomeProprio = ""
Else
'Procura a posição do primeiro espaço
lEspaco = InStr(argNome, " ")
'Se não tiver pega a posição da última letra
If lEspaco = 0 Then lEspaco = lTamanho
'Pega o primeiro nome da string
sNome = Left(argNome, lEspaco)
'Se não for aditivo converte a primeira letra
If Not InStr("e da das de do dos ", sNome) > 0 Then
sNome = UCase(Left(sNome, 1)) & LCase(Right(sNome, Len(sNome) - 1))
End If
31
'Monta o nome convertendo o restante através da recursão
nomeProprio = sNome & nomeProprio(LCase(Trim(Right(argNome, lTamanho
- lEspaco))))
End If
End Function
Método desacentua(argTexto As String) As String: recebe como argumento um texto
qualquer e devolve o mesmo texto sem acentos ou símbolos;
Function desacentua(ByVal argTexto As String) As String
'Função que retira acentos de qualquer texto.
Dim strAcento As String
Dim strNormal As String
Dim strLetra As String
Dim strNovoTexto As String
Dim intPosicao As Integer
Dim i As Integer
'Informa as duas sequências de caracteres, com e sem acento
strAcento = "ÃÁÀÂÄÉÈÊËÍÌÎÏÕÓÒÔÖÚÙÛÜÝÇÑãáàâäéèêëíìîïõóòôöúùûüýçñ"
strNormal = "AAAAAEEEEIIIIOOOOOUUUUYCNaaaaaeeeeiiiiooooouuuuycn"
'Retira os espaços antes e após
argTexto = Trim(argTexto)
'Para i de 1 até o tamanho do texto
For i = 1 To Len(argTexto)
'Retira a letra da posição atual
strLetra = Mid(argTexto, i, 1)
'Busca a posição da letra na sequência com acento
intPosicao = InStr(1, strAcento, strLetra)
'Se a posição for maior que zero
If intPosicao > 0 Then
'Retira a letra na mesma posição na
'sequência sem acentos.
strLetra = Mid(strNormal, intPosicao, 1)
End If
'Remonta o novo texto, sem acento
strNovoTexto = strNovoTexto & strLetra
Next
'Devolve o resultado
desacentua = strNovoTexto
End Function
Método abreviaNome(argNome As String) As String: recebe como argumento um texto
qualquer e devolve o mesmo texto com a penúltima parte do nome abreviada, levando
em consideração as partículas de ligação de nomes, as quais permanecem inalteradas.
32
Function abreviaNome(argNome As String) As String
'Função que abrevia o penúltimo sobrenome, levando
'em consideração os aditivos de, da, do, dos, das, e.
'Define variáveis para controle de posição e para as
'partes do nome que serão separadas e depois unidas
'novamente.
Dim ultimoEspaco As Integer, penultimoEspaco As Integer
Dim primeiraParte As String, ultimaParte As String
Dim parteNome As String
Dim tamanho As Integer, i As Integer
'Tamanho do nome passado
'no argumento
tamanho = Len(argNome)
'Loop que verifica a posição do último e do penúltimo
'espaços, utilizando apenas um loop.
For i = tamanho To 1 Step -1
If Mid(argNome, i, 1) = " " And ultimoEspaco <> 0 Then
penultimoEspaco = i
Exit For
End If
If Mid(argNome, i, 1) = " " And penultimoEspaco = 0 Then
ultimoEspaco = i
End If
Next i
'Caso i chegue a zero não podemos
'abreviar o nome
If i = 0 Then
abreviaNome = argNome
Exit Function
End If
'Separação das partes do nome em três: primeira, meio e última
primeiraParte = Left(argNome, penultimoEspaco - 1)
parteNome = Mid(argNome, penultimoEspaco + 1, ultimoEspaco -
penultimoEspaco - 1)
ultimaParte = Right(argNome, tamanho - ultimoEspaco)
'Para a montagem do nome já abreviado verificamos se a parte retirada
'não é um dos nomes de ligação: de, da ou do. Caso seja usamos o método
'recursivo para refazer os passos.
'Caso seja necessário basta acrescentar outros nomes de ligação para serem
'verificados.
If parteNome = "da" Or parteNome = "de" Or parteNome = "do" Or _
parteNome = "dos" Or parteNome = "das" Or parteNome = "e" Then
abreviaNome = abreviaNome(primeiraParte & " " & parteNome) & " " &
ultimaParte
Else
abreviaNome = primeiraParte & " " & Left(parteNome, 1) & ". " & ultimaParte
End If
End Function
A Classe ConexaoBD
33
Objetivo: oferecer funcionalidades de consulta e atualização do banco de dados. Possui
também métodos genéricos que podem ser reaproveitados em qualquer sistema. Uma
das maiores vantagens de se utilizar uma classe de conexão é que no caso de mudança
do tipo de Sistema Gerenciador de Banco de Dados (SGBD) somente ela deverá ser
alterada, permanecendo todas as outras intocadas, desde que seja mantida a interface de
comunicação dos métodos. Um exemplo seria a troca do back-end para MySql, Postgres
ou Sql Server.
Declaração de Atributo de Banco de Dados a Nível de Classe
Criaremos um atributo que será responsável por receber e disponibilizar o banco de
dados para ser utilizado pelos métodos da classe.
'Cria um database que será utilizado para toda a classe
Private db As Database
Função logicoSql(ByVal argValor As Boolean) As String: recebe como parâmetro um
valor booleano e devolve um texto contendo os valores True ou False, necessário para
códigos SQL;
Function logicoSql(ByVal argValor As Boolean) As String
'Função que troca os valores lógicos Verdadeiro/Falso
'para True/False para utilização em consultas SQL
'Se o valor for verdadeiro
If argValor Then
'Troca por True
logicoSql = "True"
Else
'Senão troca por False
logicoSql = "False"
End If
End Function
Função pontoVirgula(ByVal varValor As Variant) As String: recebe como parâmetro
um valor decimal em que o padrão de separação da parte inteira e a decimal é a vírgula,
e devolve um texto contendo o mesmo valor agora separado por ponto, necessário para
códigos SQL;
Function pontoVirgula(ByVal varValor As Variant) As String
'Função que troca a vírgula de um valor decimal por
'um ponto para utilização em consultas SQL
Dim strValor As String
Dim strInteiro As String
Dim strDecimal As String
Dim intPosicao As Integer
'Converte o valor em string
strValor = CStr(varValor)
'Busca a posição da vírgula
intPosicao = InStr(strValor, ",")
34
'Se há uma vírgula em alguma posição
If intPosicao > 0 Then
'Retira a parte inteira
strInteiro = Left(strValor, intPosicao - 1)
'Retira a parte decimal
strDecimal = Right(strValor, Len(strValor) - intPosicao)
'Junta os dois novamente incluindo
'agora o ponto no lugar da vírgula
pontoVirgula = strInteiro & "." & strDecimal
Else
'Senão devolve o mesmo valor
pontoVirgula = strValor
End If
End Function
Função dataSql(ByVal argData As Date) As String: recebe como parâmetro uma data
em qualquer formato e devolve um texto contendo a data no padrão #mm/dd/yyyy#,
necessário para códigos SQL;
Function dataSql(ByVal argData As Date) As String
'Função que formata uma data para o modo SQL
'com a cerquilha: #mm/dd/yyyy#
Dim strDia As String, strMes As String, strAno As String
'Retira dia, mês e ano
strDia = Day(argData)
strMes = Month(argData)
strAno = Year(argData)
'Remonta no formato adequado
dataSql = "#" & strMes & "/" & strDia & "/" & strAno & "#"
End Function
Função valorSql(ByVal argValor As Variant) As String: recebe como parâmetro um
valor qualquer, verifica o seu formato e devolve um valor no formato padrão dos
códigos SQL, fazendo uso das funções anteriores, caso necessário;
Function valorSql(ByVal argValor As Variant) As String
'Função que formata valores para utilização
'em consultas SQL
'Seleciona o tipo de valor informado
Select Case VarType(argValor)
'Caso seja vazio ou nulo apenas
'devolve a string Null
Case vbEmpty, vbNull
valorSql = "Null"
'Caso seja inteiro ou longo apenas
'converte em string
Case vbInteger, vbLong
valorSql = CStr(argValor)
'Caso seja simples, duplo, decimal ou moeda
35
'substitui a vírgula por ponto
Case vbSingle, vbDouble, vbDecimal, vbCurrency
valorSql = pontoVirgula(argValor)
'Caso seja data chama a função dataSql()
Case vbDate
valorSql = dataSql(argValor)
'Caso seja string acrescenta aspas simples
Case vbString
valorSql = "'" & argValor & "'"
'Caso seja lógico chama a função logicoSql()
Case vbBoolean
valorSql = logicoSql(argValor)
End Select
End Function
Função executa(codigoSql As String) As Long: recebe como argumento um texto
contendo um código SQL de inserção, exclusão ou alteração, executa o código e
devolve o número de registros afetados na operação;
Function executa(codigoSql As String) As Long
On Error GoTo Err_executa
'Atualiza dados no Banco utilizando o código Sql passado à função,
'retornando o número de registros afetados caso a operação ocorra
'com sucesso ou 0 caso ocorra algum problema
Set db = Currentdb
db.Execute codigoSql
executa = db.RecordsAffected
Exit_executa:
Set db = Nothing
Exit Function
Err_ executa:
executa = 0
Resume Exit_executa
End Function
Função consulta(codigoSql As String, Optional editavel As Boolean = False) As
Recordset: recebe como argumento um texto contendo um código SQL de consulta de
registros e um valor lógico que define o modo de bloqueio de execução, permitindo ou
não a edição dos dados durante a operação, e devolve um recordset contendo o conjunto
de registros que atenderem aos critérios;
Function consulta(codigoSql As String, Optional editavel As Boolean = False) As
Recordset
On Error GoTo Err_consulta
'Consulta os dados no Banco utilizando o código Sql passado à função,
'retornando um recordset com o resultado da consulta
Set db = Currentdb
If editavel Then
36
Set consulta = db.OpenRecordset(codigoSql, , dbInconsistent, dbOptimistic)
Else
Set consulta = db.OpenRecordset(codigoSql, dbOpenSnapshot, dbReadOnly)
End If
Exit_consulta:
Set db = Nothing
Exit Function
Err_consulta:
MsgBox "Erro SQL: " & codigoSql
Set consulta = Nothing
Resume Exit_consulta
End Function
Após criar os métodos salve as classes, mas não se esqueça que para salvar
convencionamos que seria utilizado o prefixo acl para classes auxiliares. Então
salvaremos as classes com os nomes aclUtilitario e aclConexaoBD.
Instanciação dos Objetos
Com as classes já codificadas e salvas podemos instanciá-las a partir de qualquer
módulo do nosso aplicativo. Vamos fazer um teste com cada uma das classes criadas.
Para isto voltaremos ao nosso módulo padrão TesteClasse.
Primeiro a classe Utilitario. Vamos criar o procedimento TesteUtilitario:
Sub TesteUtilitario()
'Declarando o objeto da classe Utilitario
Dim objUtil As New aclUtilitario
'Testando um método da classe, convertendo um
'nome em minúsculas para o padrão de nomes próprios
'com a primeira letra maiúscula
MsgBox objUtil.nomeProprio("plinio mabesi")
End Sub
Perceba que após digitarmos o ponto depois do nome do objeto os métodos disponíveis
são listados no menu suspenso do editor do VBA.
37
Com isso você pode digitar o nome do método ou escolher um deles na lista suspensa.
Conforme já vimos antes para executar o código basta posicionar o cursor dentro do
procedimento e pressionar F5, ou F8 para acompanhar a execução passo-a-passo.
O resultado será uma caixa de mensagem exibindo o nome informado no formato de um
nome próprio, com as iniciais maiúsculas:
Agora a classe ConexaoBD. Vamos criar o procedimento TesteConexao:
Sub TesteConexaoBD()
'Declarando o objeto da classe ConexaoBD
Dim objCon As New aclConexaoBD
'Testando um método da classe, convertendo
'uma data para o padrão SQL
MsgBox objCon.dataSql("12/06/10")
End Sub
Assim como no objeto da classe Utilitario, ao digitarmos o ponto após o nome do
objeto da classe ConexaoBD veremos a lista suspensa com os métodos disponíveis:
38
Com certeza você notou que o atributo db (Database) não foi apresentado, devido ao
fato de ser um atributo privado (Private).
Perceba também que quando digitamos o primeiro parêntese após o nome do método o
editor do VBA nos mostra quais são os parâmetros exigidos, o seu tipo e o tipo de dado
de retorno do método.
No caso do método dataSql() deveremos informar o parâmetro argData que será
passado por valor (ByVal) e é do tipo Date, e teremos como retorno uma String. Veja:
Ao executarmos este procedimento teremos como resultado uma caixa de mensagem
exibindo a data informada no padrão de utilização do SQL, conforme figura abaixo.
39
No padrão SQL veja que o mês vem em primeiro lugar, seguido pelo dia e depois pelo
ano, tudo delimitado pela cerquilha. Todos os códigos SQL devem estar com as datas
neste formato, caso contrário ocorrerá um erro e o código não será executado.
Você não só pode como deve fazer testes com os outros métodos. Crie exemplos de
utilização para cada um deles. Se encontrar dificuldade aguarde os próximos artigos,
nos quais faremos uso intenso destas duas classes. Mesmo assim o melhor é nunca
desistir...
Conclusão
Nosso sistema começa a tomar forma de um software de verdade. Nesta etapa vimos
como codificar as classes auxiliares e tivemos uma noção de como instanciá-las para
utilizar os métodos disponibilizados.
Apesar de não ter sido detalhada a criação da estrutura dos comandos e dos algoritmos
dos métodos, os comentários o ajudarão a compreender o mínimo necessário para
produzir seus próprios códigos. Caso não tenha entendido algum trecho pesquise sobre
as funções, comandos e tipos utilizados.
Conforme salientado no início deste artigo, o principal conceito que deve ser lembrado
no momento é o da reutilização de código. A reutilização se refere a qualquer
procedimento, função ou método que possa ser diretamente empregado em qualquer
projeto imediatamente, sem necessidade de ajuste ou adaptação. Com a orientação a
objetos procuramos fazer isto com classes inteiras. As classes Utilitario e ConexaoBD
são dois exemplos de classes reutilizáveis que não requerem qualquer modificação para
funcionarem em um projeto do Access.
Claro que alguém poderia dizer que estaria faltando um ou outro método ou atributo, ou
que na verdade os métodos existentes não são os melhores, mas o importante no
momento é o conceito da reutilização e não a modelagem mais eficiente e mais correta.
Entenda que após projetar a classe mais perfeita possível e codificá-la, então ela poderá
ser distribuída para ser importada e incorporada por qualquer projeto.
40
Classes V - A Classe Cliente
Chegou enfim a hora de criarmos a primeira parte funcional do nosso sistema. Depois
de todo o sistema estar modelado e também estarem codificadas as classes auxiliares,
vamos então dar vida ao programa.
A primeira classe a ser codificada será a classe Cliente. Após apresentar a estrutura da
classe faremos a implementação da interface gráfica que manipulará os dados dos
objetos.
Caso necessário relembre os conceitos anteriores sobre atributos, métodos, acesso às
propriedades do objeto, instâncias e demais ensinamentos sobre a programação
orientada a objetos, vistos nos primeiros artigos.
A Classe Cliente
O objetivo desta classe, conforme dito anteriormente, é oferecer funcionalidades de
inclusão, consulta, atualização e exclusão dos objetos do tipo cliente.
Este é o código da classe, com todos os seus atributos e métodos, que se utilizam de
funções próprias, bem como dos métodos dos objetos das classes Utilitario e
ConexaoBD.
Código da classe:
Option Compare Database
Option Explicit
'Objeto da classe Utilitario
Private objUtil As New aclUtilitario
'Atributos da Classe
'Atributo de backup e atributo identificador da Classe
'PK - Código que identifica o cliente.
Private bkpCodCliente As Variant
Private lngCodCliente As Variant
'Classe social do cliente, calculada de acordo com a renda.
Private strClasse As Variant
'CPF do cliente.
Private strCpf As Variant
'E-mail do cliente.
Private strEmail As Variant
'Nome completo do cliente.
Private strNomeCliente As Variant
'Renda mensal do cliente, em reais.
Private curRenda As Variant
'Métodos Get, Set e Let da Classe
41
Property Get codCliente() As Variant
codCliente = lngCodCliente
End Property
Property Let codCliente(argCodCliente As Variant)
lngCodCliente = argCodCliente
If IsEmpty(bkpCodCliente) Then
bkpCodCliente = lngCodCliente
End If
End Property
Property Get classe() As Variant
classe = strClasse
End Property
Property Get cpf() As Variant
cpf = strCpf
End Property
Property Let cpf(argCpf As Variant)
If Not IsNull(argCpf) Then
If Not objUtil.validaCPF(argCpf) Then
MsgBox "O CPF <" & Format(argCpf, "000\.000\.000\-00") & _
"> não é válido.", vbExclamation, "CPF Inválido"
strCpf = Null
Else
strCpf = argCpf
End If
Else
strCpf = argCpf
End If
End Property
Property Get email() As Variant
email = strEmail
End Property
Property Let email(argEmail As Variant)
If Not IsNull(argEmail) Then
If Not objUtil.validaEmail(argEmail) Then
If MsgBox("O E-mail <" & argEmail & "> não possui um formato
válido." _
& vbCrLf & "Deseja incluir este e-mail?", vbQuestion + vbYesNo,
_
"E-mail Inválido") = vbYes Then
strEmail = argEmail
42
End If
Else
strEmail = argEmail
End If
Else
strEmail = argEmail
End If
End Property
Property Get nomeCliente() As Variant
nomeCliente = strNomeCliente
End Property
Property Let nomeCliente(argNomeCliente As Variant)
If Not IsNull(argNomeCliente) Then
strNomeCliente = objUtil.nomeProprio(argNomeCliente)
Else
strNomeCliente = argNomeCliente
End If
End Property
Property Get renda() As Variant
renda = curRenda
End Property
Property Let renda(argRenda As Variant)
curRenda = argRenda
'Alterar classe quando o valor da
'renda for alterado
strClasse = calculaClasse()
End Property
'Método Existe [Com conhecimento de SQL]
'Verifica a existência do objeto Cliente na tabela correspondente
'no Banco de Dados
Function existe(argCodCliente As Variant) As Boolean
On Error GoTo Err_existe
Dim objCon As New aclConexaoBD
Dim rstExiste As Recordset
Dim strSql As String
existe = False
strSql = "Select * " & _
"From Cliente " & _
"Where codCliente = " & objCon.valorSql(argCodCliente)
Set rstExiste = objCon.consulta(strSql)
If rstExiste.RecordCount > 0 Then
existe = True
43
End If
'Fecha o Recordset existe
rstExiste.Close
Exit_existe:
Set rstExiste = Nothing
Exit Function
Err_existe:
existe = False
GoTo Exit_existe
End Function
'Método Incluir [Com conhecimento de SQL]
'Inclui um novo objeto na tabela correspondente dentro do Banco de
dados
Private Function incluir() As Boolean
On Error GoTo Err_incluir
Dim objCon As New aclConexaoBD
Dim strSql As String
strSql = "Insert Into " & _
"Cliente(codCliente,classe,cpf,email,nomeCliente,renda) " & _
"Values(" & objCon.valorSql(codCliente) & "," _
& objCon.valorSql(classe) & "," & objCon.valorSql(cpf) _
& "," & objCon.valorSql(email) & "," &
objCon.valorSql(nomeCliente) _
& "," & objCon.valorSql(renda) & ")"
incluir = (objCon.executa(strSql) > 0)
If incluir Then
'Atualiza os campos de backup
bkpCodCliente = codCliente
End If
Exit_incluir:
Exit Function
Err_incluir:
incluir = False
GoTo Exit_incluir
End Function
'Método Excluir [Com conhecimento de SQL]
'Exclui o objeto atual na tabela correspondente dentro do Banco de
dados
Function excluir() As Boolean
On Error GoTo Err_excluir
Dim objCon As New aclConexaoBD
Dim strSql As String
strSql = "Delete From Cliente " & _
"Where codCliente = " & objCon.valorSql(codCliente)
excluir = (objCon.executa(strSql) > 0)
44
Exit_excluir:
Exit Function
Err_excluir:
excluir = False
GoTo Exit_excluir
End Function
'Método Obter [Com conhecimento de SQL]
'Recupera o objeto Cliente através dos argumentos informados
Function obter(argCodCliente As Variant) As Boolean
On Error GoTo Err_obter
Dim objCon As New aclConexaoBD
Dim rstObter As Recordset
Dim strSql As String
strSql = "Select * " & _
"From Cliente " & _
"Where codCliente = " & objCon.valorSql(argCodCliente)
Set rstObter = objCon.consulta(strSql)
If rstObter.RecordCount = 0 Then
obter = False
Exit Function
End If
'Atualiza os campos de backup e os identificadores
codCliente = argCodCliente
bkpCodCliente = argCodCliente
'Atualiza os campos restantes
strClasse = rstObter.Fields("classe")
cpf = rstObter.Fields("cpf")
strEmail = rstObter.Fields("email")
nomeCliente = rstObter.Fields("nomeCliente")
renda = rstObter.Fields("renda")
obter = True
'Fecha o Recordset obter
rstObter.Close
Exit_obter:
Set rstObter = Nothing
Exit Function
Err_obter:
obter = False
MsgBox Err.Description
GoTo Exit_obter
End Function
'Método Salvar [Com conhecimento de SQL]
'Salva o objeto atual na tabela correspondente dentro do Banco de
dados
45
Function salvar() As Boolean
On Error GoTo Err_salvar
Dim objCon As New aclConexaoBD
Dim strSql As String
If existe(bkpCodCliente) Then
strSql = "Update Cliente " & _
"Set codCliente = " & objCon.valorSql(codCliente) _
& ", classe = " & objCon.valorSql(classe) _
& ", cpf = " & objCon.valorSql(cpf) & ", email = " _
& objCon.valorSql(email) & ", nomeCliente = " _
& objCon.valorSql(nomeCliente) & ", renda = " _
& objCon.valorSql(renda) _
& " Where codCliente = " & objCon.valorSql(bkpCodCliente)
salvar = (objCon.executa(strSql) > 0)
Else
salvar = incluir
End If
If salvar Then
'Atualiza as variáveis de backup com o novo valor da chave
bkpCodCliente = codCliente
End If
Exit_salvar:
Exit Function
Err_salvar:
salvar = False
GoTo Exit_salvar
End Function
'Método calculaClasse
'Calcula a classe social do objeto atual baseado na
'renda informada e devolve uma letra correspondente
'à sua classificação dentro das faixas salariais.
Private Function calculaClasse() As String
If renda >= 15300 Then
calculaClasse = "A"
ElseIf renda >= 7650 Then
calculaClasse = "B"
ElseIf renda >= 3060 Then
calculaClasse = "C"
ElseIf renda >= 1020 Then
calculaClasse = "D"
ElseIf renda > 0 Then
calculaClasse = "E"
Else
calculaClasse = ""
End If
End Function
'Fim da classe...
Pontos Importantes
46
Nesta primeira classe referente a um objeto atualizável, com atributos modificáveis,
passíveis de serem persistidos, podemos notar alguns pontos de interesse para o
aprendizado da metodologia, conforme a seguir.
1. Atributos do tipo Variant: Como podemos encontrar objetos cujos campos não são
de preenchimento obrigatório devemos manter a possibilidade de atribuir valores nulos
aos atributos dos objetos, os quais serão reconhecidos pelo método valorSql(), da classe
ConexaoBD, e convertidos da maneira correta para armazenagem na tabela. Caso
utilizássemos atributos com os tipos pré-definidos não teríamos como reconhecer um
atributo nulo de maneira tão fácil. Neste caso teríamos que procurar métodos
alternativos de convenção e tratamento de valores que representariam a nulidade de
atributos, como por exemplo uma seqüência de comprimento zero em uma string, ou um
valor negativo que provavelmente nunca seria atribuído a um campo de código para
chave primária, entre outros. Contudo, para o nosso projeto, contando com a facilidade
da atribuição de valores e seus tipos às variáveis do tipo Variant, e a funcionalidade do
método valorSql() esta será a forma que adotaremos.
'Nome completo do cliente.
Private strNomeCliente As Variant
2. Criação do atributo de backup do objeto: Apesar de não ser necessário quando se
usa atributos identificadores do tipo auto-numeração, esta é uma técnica que possibilita
a alteração do código identificador do objeto permitindo salvar as modificações
posteriormente, já que o antigo valor, que continua armazenado no banco de dados,
ainda estará disponível no atributo de backup. Quando se usa qualquer tipo de código
personalizado como a chave primária este recurso torna-se indispensável.
'Atributo de backup e atributo identificador da Classe
'PK - Código que identifica o cliente.
Private bkpCodCliente As Variant
Private lngCodCliente As Variant
3. Validação de dados no momento da atribuição do valor: Neste caso podemos
visualizar na prática como validar um dado antes de atribuí-lo ao objeto. Como exemplo
verificamos a validade de um CPF fornecido pelo usuário, permitindo ou não a sua
utilização. Esta é uma tarefa que evita falhas de digitação e incorreções nos dados
armazenados.
Property Let cpf(argCpf As Variant)
If Not IsNull(argCpf) Then
If Not objUtil.validaCPF(argCpf) Then
MsgBox "O CPF <" & Format(argCpf, "000\.000\.000\-00") _
& "> não é válido.", vbExclamation, "CPF Inválido"
strCpf = Null
Else
strCpf = argCpf
End If
Else
strCpf = argCpf
End If
End Property
47
4. Conversão de dados no momento da atribuição do valor: Neste caso podemos
visualizar na prática como alterar um dado fornecido pelo usuário antes de atribuí-lo ao
objeto. Esta tarefa é útil na padronização das informações armazenadas. Como exemplo
armazenamos todos os nomes dos clientes no padrão de nomes próprios, com a primeira
letra de cada parte do nome em maiúsculas e o restante em minúsculas. Esta
funcionalidade é oferecida pelo método nomeProprio() da classe Utilitario,
representada pelo objeto objUtil.
Property Let nomeCliente(argNomeCliente As Variant)
If Not IsNull(argNomeCliente) Then
strNomeCliente = objUtil.nomeProprio(argNomeCliente)
Else
strNomeCliente = argNomeCliente
End If
End Property
5. Determinação de uma propriedade baseada em outra: Apesar de não ser uma boa
prática e estar diretamente contra as Formas Normais, esta funcionalidade foi colocada
apenas para servir de exemplo. Aqui determinamos a classe social a que o cliente
pertence a partir da sua renda informada. Para isto utilizamos o método calculaClasse()
da própria classe Cliente. A informação é atribuída à propriedade classe e armazenada
no banco de dados, lembrando mais uma vez que esta não é uma prática recomendada
em sistemas profissionais.
Property Let renda(argRenda As Variant)
curRenda = argRenda
'Alterar classe quando o valor da
'renda for alterado
strClasse = calculaClasse()
End Property
6. Ajuste de código SQL: Para que possamos montar nossas classes sem nos preocupar
com o formato SQL dos dados, utilizamos a classe ConexaoBD e seus métodos para
fazer todos os ajustes e nos deixar resolver os problemas mais sérios, que requerem
nossa atenção. Veja que para nós não será necessário descobrir se o atributo e do tipo
texto, data ou numérico. O método valorSql() se encarrega disto. Aqui a classe está
representada pelo objeto objCon.
strSql = "Select * " & _
"From Cliente " & _
"Where codCliente = " & objCon.valorSql(argCodCliente)
7. Atributo classe: Para o cálculo da classe social a partir da renda foi utilizada uma
tabela fictícia de exemplo, a qual não representa fielmente os valores publicados pelos
órgãos competentes.
Outra funcionalidade importante que podemos encontrar nos códigos é o tratamento de
erros com a instrução On Error. Apesar de não ser detalhado neste curso é altamente
recomendado que o leitor pesquise e compreenda a sua utilização. Foram colocados
exemplos simples nos métodos, porém o correto entendimento deste recurso possibilita
48
alta capacidade de resolução de problemas na programação, evitando que possíveis e
prováveis erros no projeto ou na implementação sejam visíveis para o usuário final de
maneira inadequada.
Mais Funções Auxiliares
Neste projeto não utilizaremos códigos com auto-numeração diretamente nas tabelas.
Sendo assim necessitaremos de uma função que realize esta tarefa.
Criaremos então um módulo padrão que conterá todas as funções auxiliares que
necessitarmos durante o curso. Ele se chamará Funcoes e nossa primeira função será
chamada proximoCodigo().
Este será o código da função:
Function proximoCodigo(argCampo As String, argTabela As String, _
Optional criterio As String = "") As Long
proximoCodigo = Nz(DMax(argCampo, argTabela, criterio), 0) + 1
End Function
Ela receberá como parâmetros o nome do campo, o nome da tabela e um critério de
busca opcional, devolvendo um número que será o próximo código livre daquele campo
numérico da tabela informada.
Desta maneira a função será genérica e servirá para qualquer tabela que contenha um
campo chave de código numérico ordenado.
A Interface Gráfica
Para que possamos visualizar e manipular os dados dos clientes devemos criar um
formulário com campos para preenchimento e botões de comando que coordenem as
ações sobre os dados.
A seguir serão definidas as características para o formulário no qual iremos trabalhar
com os dados dos objetos relativos aos clientes. As especificações de design são apenas
sugestões, porém as informações de origem de dados, bloqueio de campos e formatos de
dados são obrigatórias, sob pena de não obter a funcionalidade desejada. Estas
especificações estarão marcadas com um asterisco vermelho ao lado.
No formulário teremos uma lista contendo os clientes cadastrados, um campo de texto
para realizar buscas, alguns campos de texto para digitação das informações e os botões
de comando para criar, salvar e excluir registros.
Obs: Somente as propriedades que foram alteradas para um valor diferente do padrão
serão apresentadas. Para as demais utilize o padrão do sistema ou o valor que desejar,
desde que não comprometa a funcionalidade do sistema.
Formulário
Nome: FCliente
Largura: 20cm
49
Altura: Cabeçalho (1cm) / Detalhe (10cm)
Legenda: Sistema de Vendas
Estilo da borda: fino
Seletores de registro: Não
Botões de navegação: Não
Linhas divisórias: Não
Barras de rolagem: Nenhuma
Caixa de controle: Sim
Botão Fechar: Sim
Botões Min Max: Nenhum
Popup: Sim
Janela Restrita: Não
Controle Listbox
Nome: lstCliente *
Origem da linha: SELECT Cliente.codCliente, Cliente.nomeCliente,
Format(Cliente.cpf,"000\.000\.000-00") AS cpf FROM
Cliente
ORDER BY Cliente.nomeCliente; *
Tipo de Origem da Linha: Tabela/Consulta *
Coluna acoplada: 1 *
Número de colunas: 3 *
Largura das colunas: 0cm;5cm;3cm *
Largura: 8cm
Cor do fundo: Cinza claro
Controles Textbox
Nome: txtPesquisa *
Cor do fundo: Amarelo
Alinhamento: Esquerda
Nome: txtCodigo *
Ativado: Não *
Bloqueado: Sim *
Cor do fundo: Cinza
Alinhamento: Centro
Nome: txtCpf *
Cor do fundo: Azul claro
Máscara de entrada: 000.000.000\-00;;_ *
Alinhamento: Centro
Nome: txtNome *
Cor do fundo: Azul claro
Alinhamento: Esquerda
Nome: txtEmail *
Cor do fundo: Azul claro
Alinhamento: Esquerda
Nome: txtRenda *
Cor do fundo: Azul claro
Alinhamento: Direita
Formato: Unidade Monetária
Nome: txtClasse *
Ativado: Não *
50
Bloqueado: Sim *
Cor do fundo: Cinza
Alinhamento: Centro
Botões de Comando
Nome: btnNovo *
Legenda: Novo
Cor da Fonte: Azul escuro
Nome: btnSalvar *
Legenda: Salvar
Cor da Fonte: Azul escuro
Nome: btnExcluir *
Legenda: Excluir
Cor da Fonte: Vermelho escuro
Como sugestão de design os campos deverão estar posicionados conforme demonstrado
na figura a seguir, assim como devem ser adicionadas as legendas dos campos, as quais
não foram descritas anteriormente:
Códigos do Formulário
Para que nosso formulário seja capaz de manipular os dados dos objetos cliente
deveremos implementar as funcionalidades necessárias que permitam criar um novo
cliente, buscar um cliente já cadastrado, editar seus dados, salvá-lo ou excluí-lo.
Sendo assim criaremos algumas funções genéricas que sejam úteis para quantos
procedimentos delas necessitem, reaplicando o conceito da reutilização de código. Além
51
disso incluiremos os códigos dos campos que possuem eventos, além dos botões de
comando.
Todos os códigos a seguir deverão ser colocados no módulo do formulário FCliente.
1. Pesquisa de clientes: Para facilitar a busca de clientes por qualquer parte do nome
utilizaremos um campo de texto cujo valor será o elemento condicional para o código
SQL que será atribuído ao controle lstCliente, atualizando sua propriedade Origem da
Linha após cada alteração realizada no campo de pesquisa. Para isto devemos colocar o
código no evento Ao Alterar do campo txtPesquisa.
Private Sub txtPesquisa_Change()
lstCliente.RowSource = "SELECT Cliente.codCliente,
Cliente.nomeCliente, " & _
"Format(Cliente.cpf,'000\.000\.000-00') AS cpf " & _
"FROM Cliente " & _
"WHERE nomeCliente Like '*" & txtPesquisa.Text & "*' " &
_
"ORDER BY Cliente.nomeCliente;"
lstCliente.Requery
End Sub
2. Limpeza dos campos: Para efetuar a limpeza dos campos de preenchimento
utilizaremos um procedimento genérico cuja função será a de atribuir o valor nulo a
todos os campos de texto e enviar o foco ao campo txtCpf.
Private Sub limpaCampos()
txtCodigo = Null
txtCpf = Null
txtNome = Null
txtEmail = Null
txtRenda = Null
txtClasse = Null
txtCpf.SetFocus
End Sub
3. Novo registro: Para a criação de um novo registro em branco simplesmente iremos
chamar o procedimento limpaCampos() no evento Ao Clicar do botão btnNovo.
Private Sub btnNovo_Click()
Call limpaCampos
End Sub
4. Atualização de campos: Assim como fizemos um procedimento genérico que limpa
os campos para a criação de um novo registro teremos também um que preencha os
campos com as informações de um objeto cliente passadas como parâmetro, ou seja, o
procedimento recebe um objeto completo e lança os valores de seus atributos nos
campos do formulário.
Private Sub atualizaCampos(argCliente As clsCliente)
52
txtCodigo = argCliente.codCliente
txtCpf = argCliente.cpf
txtNome = argCliente.nomeCliente
txtEmail = argCliente.email
txtRenda = argCliente.renda
txtClasse = argCliente.classe
End Sub
5. Escolha de cliente: Já que teremos também que trabalhar com clientes já
cadastrados, devemos implementar alguma maneira de recuperar suas informações.
Faremos isto utilizando a Listbox lstCliente, que utilizará o procedimento
atualizaCampos() para preencher os campos com as informações do objeto do cliente
que foi clicado.
Private Sub lstCliente_Click()
Dim objCliente As New clsCliente
Dim codigoCliente As Long
codigoCliente = lstCliente.Value
If objCliente.obter(codigoCliente) Then
Call atualizaCampos(objCliente)
End If
End Sub
6. Montagem de um objeto: Esta também é uma função genérica que será utilizada por
outros procedimentos ou funções. Seu objetivo é criar um novo objeto cliente, coletar os
dados digitados nos campos, atribuir ao objeto criado, e devolvê-lo para quem chamou a
função.
Private Function buscaCampos() As clsCliente
Set buscaCampos = New clsCliente
If IsNull(txtCodigo) Then
txtCodigo = proximoCodigo("codCliente", "Cliente")
End If
buscaCampos.codCliente = txtCodigo
buscaCampos.cpf = txtCpf
buscaCampos.nomeCliente = txtNome
buscaCampos.email = txtEmail
buscaCampos.renda = txtRenda
End Function
7. Salvando um cliente: Depois de informados os dados de um novo cliente, ou
alterados os dados de um cliente já cadastrado, necessitaremos de um procedimento que
efetue a gravação destes dados. Para isto o botão btnSalvar conterá o código necessário
no evento Ao Clicar. Ele fará uso da função buscaCampos(), a qual montará o objeto a
ser salvo, e também o método salvar() do objeto, apresentando uma mensagem ao
usuário com o resultado da operação. Após salvar os dados de um cliente também
53
deveremos atualizar a Listbox para que apresente estes dados, sejam eles novos ou
apenas alterados.
Private Sub btnSalvar_Click()
Dim objCliente As clsCliente
If Not IsNull(txtCpf) And Not IsNull(txtNome) Then
Set objCliente = buscaCampos
If objCliente.salvar Then
MsgBox "O cliente foi salvo com sucesso.", vbInformation, _
"Salvar Cliente"
lstCliente.Requery
Call atualizaCampos(objCliente)
Else
MsgBox "Ocorreu um erro durante o salvamento.", vbExclamation, _
"Salvar Cliente"
End If
Else
MsgBox "Informe os dados do cliente.", vbExclamation, "Salvar
Cliente"
End If
End Sub
8. Excluindo um cliente: Além de criar e alterar os dados de um cliente, é importante
que também possamos excluí-lo definitivamente de nosso cadastro. Logo devemos
também implementar a funcionalidade para o botão btnExcluir, que conterá o código
necessário também no evento Ao Clicar. Ele também deverá fazer uso da função
buscaCampos() para a montagem do objeto a ser excluído, pois caso contrário a classe
não saberá que objeto deve ser excluído. Em seguida utilizaremos o método excluir() do
objeto, apresentando uma mensagem ao usuário com o resultado da operação. É claro
que após excluir um cliente também deveremos atualizar a Listbox para que a mesma
reflita as alterações realizadas.
Private Sub btnExcluir_Click()
Dim objCliente As clsCliente
If Not IsNull(txtCodigo) Then
If MsgBox("Confirma a exclusão do registro?", vbQuestion +
vbYesNo, _
"Excluir Cliente") = vbYes Then
Set objCliente = buscaCampos
If objCliente.excluir Then
MsgBox "O registro foi excluído com sucesso.", vbInformation,
_
"Excluir Cliente"
lstCliente.Requery
Call limpaCampos
Else
MsgBox "Ocorreu um erro durante a exclusão.", vbExclamation, _
"Excluir Cliente"
End If
54
End If
End If
End Sub
Estes são os procedimentos e funções básicas para que a interface possa manipular os
objetos. Não se esqueça de que para efetuar os testes deveremos utilizar um CPF válido
devido à verificação realizada.
Sistema de Exemplo
A partir deste artigo será disponibilizado o link para download do sistema de vendas no
estado de desenvolvimento em que se encontrar o projeto.
Recomendo aos estudiosos desenvolvedores que o consultem apenas para tirar dúvidas e
realizar comparações. Aqueles que realmente quiserem aprender façam toda a
codificação e a montagem das telas manualmente. Somente assim a sua mente irá se
defrontar com as dificuldades do processo e assimilará os passos necessários para a
resolução dos problemas e a conclusão dos objetivos.
Segue o link para download:
Venda00.zip
Como ainda não há uma tela principal abra o formulário FCliente diretamente na janela
Banco de Dados (Access 2003-) ou no Painel de Navegação (Access 2007+).
Conclusão
Enfim demos início à parte mais empolgante do curso, na qual podemos ver o sistema
realmente operando, manipulando os objetos. Pudemos ver como se dá a interação entre
objetos, a verificação de atributos, a montagem de códigos SQL para busca, alteração,
inserção e exclusão de dados, além de visualizar procedimentos simples de tratamento
de erros.
Nunca é demais relembrar que o objetivo maior de todas as etapas do curso é apresentar
de forma didática, da maneira mais simples possível, a utilização de objetos em projetos
do Access/VBA. Sendo assim não considere as implementações apresentadas como o
padrão de fato da programação. Muita coisa foi simplesmente omitida ou modificada,
mesmo partindo dos próprios padrões que utilizo em meus projetos reais para que fosse
alcançada a objetividade necessária ao entendimento por parte de usuários iniciantes ou
com pouca experiência na POO.
A lição mais importante deste artigo foi realmente a visualização da dinâmica da
utilização dos objetos na manipulação de informações, tanto na sua apresentação quanto
na sua persistência.
A partir deste momento, creio, ficará cada vez mais fácil a percepção da operação com
objetos, desde que o leitor compreenda bem o que foi visto até agora, pois estes
55
conceitos iniciais são a base da orientação a objetos. Apesar de não contarmos com as
outras faces da OO, como a herança por exemplo, com certeza a mente do leitor já
começará a visualizar melhor as infinitas possibilidades que a metodologia
fornece. Porém relembro que para conseguir dominar a técnica é imprescindível
continuar treinando, criando seus próprios códigos, fazendo experiências, resolvendo
problemas com a aplicação do paradigma, lendo outros materiais e compartilhando
conhecimento, seja com colegas de trabalho, amigos ou em fóruns sobre o assunto.
56
Classes VI - A Classe Produto
Continuando a inclusão de funcionalidade em nosso sistema vamos agora ao próximo
passo.
A próxima classe a ser codificada será a classe Produto. Após apresentar a estrutura da
classe faremos a implementação da interface gráfica que manipulará os dados dos
objetos.
Mais uma vez, caso necessário, relembre os conceitos anteriores sobre atributos,
métodos, acesso às propriedades do objeto, instâncias e demais ensinamentos sobre a
programação orientada a objetos, vistos nos primeiros artigos.
A Classe Produto
O objetivo desta classe, conforme dito anteriormente, é oferecer funcionalidades de
inclusão, consulta, atualização e exclusão dos objetos do tipo produto.
Este é o código da classe, com todos os seus atributos e métodos, que se utilizam de
funções próprias, bem como dos métodos do objeto da classe ConexaoBD.
Código da classe:
Option Compare Database
Option Explicit
'Atributos da Classe
'Atributo de backup e atributo identificador da Classe
'PK - Código que identifica o produto.
Private bkpCodProduto As Variant
Private lngCodProduto As Variant
'Descrição do produto.
Private strDescricao As Variant
'Quantidade mínima desejada no estoque.
Private dblEstoqueMinimo As Variant
'Quantidade atual do estoque.
Private dblQtdEstoque As Variant
'Unidade de comercialização do produto.
Private strUnidade As Variant
'Valor unitário do produto.
Private curValorUnitario As Variant
'Métodos Get, Set e Let da Classe
Property Get codProduto() As Variant
57
codProduto = lngCodProduto
End Property
Property Let codProduto(argCodProduto As Variant)
lngCodProduto = argCodProduto
If IsEmpty(bkpCodProduto) Then
bkpCodProduto = lngCodProduto
End If
End Property
Property Get descricao() As Variant
descricao = strDescricao
End Property
Property Let descricao(argDescricao As Variant)
strDescricao = argDescricao
End Property
Property Get estoqueMinimo() As Variant
estoqueMinimo = dblEstoqueMinimo
End Property
Property Let estoqueMinimo(argEstoqueMinimo As Variant)
dblEstoqueMinimo = argEstoqueMinimo
End Property
Property Get qtdEstoque() As Variant
qtdEstoque = dblQtdEstoque
End Property
Property Let qtdEstoque(argQtdEstoque As Variant)
dblQtdEstoque = argQtdEstoque
End Property
Property Get unidade() As Variant
unidade = strUnidade
End Property
Property Let unidade(argUnidade As Variant)
strUnidade = Ucase(argUnidade)
End Property
58
Property Get valorUnitario() As Variant
valorUnitario = curValorUnitario
End Property
Property Let valorUnitario(argValorUnitario As Variant)
curValorUnitario = argValorUnitario
End Property
'Método Existe [Com conhecimento de SQL]
'Verifica a existência do objeto Produto na tabela
'correspondente no Banco de Dados.
Function existe(argCodProduto As Variant) As Boolean
On Error GoTo Err_existe
Dim objCon As New aclConexaoBD
Dim rstExiste As Recordset
Dim strSql As String
existe = False
strSql = "Select * " & _
"From Produto " & _
"Where codProduto = " & objCon.valorSql(argCodProduto)
Set rstExiste = objCon.consulta(strSql)
If rstExiste.RecordCount > 0 Then
existe = True
End If
'Fecha o Recordset existe
rstExiste.close
Exit_existe:
Set rstExiste = Nothing
Exit Function
Err_existe:
existe = False
GoTo Exit_existe
End Function
'Método Incluir [Com conhecimento de SQL]
'Inclui um novo objeto na tabela correspondente dentro do Banco de
dados
Function incluir() As Boolean
On Error GoTo Err_incluir
Dim objCon As New aclConexaoBD
Dim strSql As String
strSql = "Insert Into " & _
"Produto(codProduto,descricao,estoqueMinimo,qtdEstoque, _
unidade,valorUnitario) " & _
"Values(" & objCon.valorSql(codProduto) & "," & _
59
objCon.valorSql(descricao) & "," & _
objCon.valorSql(estoqueMinimo) & "," & _
objCon.valorSql(qtdEstoque) & "," & _
objCon.valorSql(unidade) & "," & _
objCon.valorSql(valorUnitario) & ")"
incluir = (objCon.executa(strSql) > 0)
If incluir Then
'Atualiza os campos de backup
bkpCodProduto = codProduto
End If
Exit_incluir:
Exit Function
Err_incluir:
incluir = False
GoTo Exit_incluir
End Function
'Método Excluir [Com conhecimento de SQL]
'Exclui o objeto atual na tabela correspondente dentro do Banco de
dados
Function excluir() As Boolean
On Error GoTo Err_excluir
Dim objCon As New aclConexaoBD
Dim strSql As String
strSql = "Delete From Produto " & _
"Where codProduto = " & objCon.valorSql(codProduto)
excluir = (objCon.executa(strSql) > 0)
Exit_excluir:
Exit Function
Err_excluir:
excluir = False
GoTo Exit_excluir
End Function
'Método Obter [Com conhecimento de SQL]
'Recupera o objeto Produto através dos argumentos informados
Function obter(argCodProduto As Variant) As Boolean
On Error GoTo Err_obter
Dim objCon As New aclConexaoBD
Dim rstObter As Recordset
Dim strSql As String
strSql = "Select * " & _
"From Produto " & _
"Where codProduto = " & objCon.valorSql(argCodProduto)
Set rstObter = objCon.consulta(strSql)
If rstObter.RecordCount = 0 Then
60
obter = False
Exit Function
End If
'Atualiza os campos de backup e os identificadores
codProduto = argCodProduto
bkpCodProduto = argCodProduto
'Atualiza os campos restantes
descricao = rstObter.Fields("descricao")
estoqueMinimo = rstObter.Fields("estoqueMinimo")
qtdEstoque = rstObter.Fields("qtdEstoque")
unidade = rstObter.Fields("unidade")
valorUnitario = rstObter.Fields("valorUnitario")
obter = True
'Fecha o Recordset obter
rstObter.close
Exit_obter:
Set rstObter = Nothing
Exit Function
Err_obter:
obter = False
GoTo Exit_obter
End Function
'Método Salvar [Com conhecimento de SQL]
'Salva o objeto atual na tabela correspondente dentro do Banco de
dados
Function salvar() As Boolean
On Error GoTo Err_salvar
Dim objCon As New aclConexaoBD
Dim strSql As String
If existe(bkpCodProduto) Then
strSql = "Update Produto " & _
"Set codProduto = " & objCon.valorSql(codProduto) & _
", descricao = " & objCon.valorSql(descricao) & _
", estoqueMinimo = " & objCon.valorSql(estoqueMinimo)
& _
", qtdEstoque = " & objCon.valorSql(qtdEstoque) & _
", unidade = " & objCon.valorSql(unidade) & _
", valorUnitario = " & objCon.valorSql(valorUnitario)
& _
" Where codProduto = " & objCon.valorSql(bkpCodProduto)
salvar = (objCon.executa(strSql) > 0)
Else
salvar = incluir
End If
If salvar Then
'Atualiza as variáveis de backup com o novo valor da chave
bkpCodProduto = codProduto
End If
61
Exit_salvar:
Exit Function
Err_salvar:
salvar = False
GoTo Exit_salvar
End Function
'Método baixarEstoque()
'Atualiza o estoque diminuindo a quantidade informada como parâmetro
'e devolve um valor booleano que indica o sucesso da operação;
Function baixarEstoque(argQtd As Double) As Boolean
If dblQtdEstoque - Abs(argQtd) < 0 Then
baixarEstoque = False
Else
dblQtdEstoque = dblQtdEstoque - Abs(argQtd)
baixarEstoque = salvar
End If
End Function
'Método subirEstoque()
'Atualiza o estoque acrescentando a quantidade informada como
'parâmetro e devolve um valor booleano que indica o sucesso
'da operação;
Function subirEstoque(argQtd As Double) As Boolean
dblQtdEstoque = dblQtdEstoque + Abs(argQtd)
subirEstoque = salvar
End Function
'Método estoqueBaixo
'Verifica o estoque atual e compara com o valor de estoque mínimo
'cadastrado para produto e devolve um valor booleano que indica
'se o valor atual está abaixo do previsto.
Function estoqueBaixo() As Boolean
If dblQtdEstoque < dblEstoqueMinimo Then
estoqueBaixo = True
End If
End Function
'Fim da classe...
Pontos Importantes
Desta vez, complementando os itens tratados no artigo anterior, vamos destacar o
pontos a seguir.
1. Controle do estoque: Ao baixarmos o estoque de um produto, devemos verificar se é
possível diminuir a quantidade informada, pois seria impossível retirar mais do que a
quantidade existente em estoque. Além disso, não podemos baixar quantidades
negativas de produto, pois isto significa que estaríamos aumentando a quantidade ao
invés de diminuir. O método somente retornará True se for possível realizar a operação.
62
Function baixarEstoque(argQtd As Double) As Boolean
If dblQtdEstoque - Abs(argQtd) <= 0 Then
baixarEstoque = False
Else
dblQtdEstoque = dblQtdEstoque - Abs(argQtd)
baixarEstoque = salvar
End If
End Function
2. Conversão de dados no momento da atribuição do valor: Para a atribuição de
algumas propriedades será realizada uma conversão dos dados. No caso da unidade o
valor será convertido em letras maiúsculas para que fique padronizado. Para os atributos
que armazenas a quantidade de produto em estoque e estoque mínimo não serão aceitos
valores negativos, sendo assim todas as quantidades serão convertidas para seus
respectivos valores absolutos.
Property Let estoqueMinimo(argEstoqueMinimo As Variant)
dblEstoqueMinimo = Abs(argEstoqueMinimo)
End Property
Property Let qtdEstoque(argQtdEstoque As Variant)
dblQtdEstoque = Abs(argQtdEstoque)
End Property
Property Let unidade(argUnidade As Variant)
strUnidade = UCase(argUnidade)
End Property
A Interface Gráfica
Assim como no artigo anterior, para que possamos visualizar e manipular os dados dos
produtos devemos criar um formulário com campos para preenchimento e botões de
comando que coordenem as ações sobre os dados.
A seguir serão definidas as características para o formulário no qual iremos trabalhar
com os dados dos objetos relativos aos produtos. Neste caso as especificações de design
também são apenas sugestões, porém as informações de origem de dados, bloqueio de
campos e formatos de dados são obrigatórias, sob pena de não obter a funcionalidade
desejada. Estas especificações estarão marcadas com um asterisco vermelho ao lado.
No formulário teremos uma lista contendo os produtos cadastrados, um campo de texto
para realizar buscas, alguns campos de texto para digitação das informações e os botões
de comando para criar, salvar e excluir registros.
63
Obs: Somente as propriedades que foram alteradas para um valor diferente do padrão
serão apresentadas. Para as demais utilize o padrão do sistema ou o valor que desejar,
desde que não comprometa a funcionalidade do sistema.
Formulário
Nome: FProduto
Largura: 19cm
Altura: Cabeçalho (1cm) / Detalhe (10cm)
Legenda: Sistema de Vendas
Estilo da borda: fino
Seletores de registro: Não
Botões de navegação: Não
Linhas divisórias: Não
Barras de rolagem: Nenhuma
Caixa de controle: Sim
Botão Fechar: Sim
Botões Min Max: Nenhum
Popup: Sim
Janela Restrita: Não
Controle Listbox
Nome: lstProduto *
Origem da linha: SELECT Produto.codProduto, Produto.descricao,
Produto.unidade, Produto.valorUnitario,
Produto.estoqueMinimo,
Produto.qtdEstoque FROM Produto ORDER BY
Produto.descricao; *
Tipo de Origem da Linha: Tabela/Consulta *
Coluna acoplada: 1 *
Número de colunas: 6 *
Largura das colunas: 0cm;5cm;1cm;2cm;1cm;1cm *
Largura: 10cm
Cor do fundo: Cinza claro
Controles Textbox
Nome: txtPesquisa *
Cor do fundo: Amarelo
Alinhamento: Esquerda
Nome: txtCodigo *
Ativado: Não *
Bloqueado: Sim *
Cor do fundo: Cinza
Alinhamento: Centro
Nome: txtDescricao *
Cor do fundo: Azul claro
Alinhamento: Esquerda
Nome: txtUnidade *
Cor do fundo: Azul claro
Alinhamento: Esquerda
Nome: txtValorUnitario *
Cor do fundo: Azul claro
Alinhamento: Direita
64
Formato: Unidade Monetária
Nome: txtQtdEstoque *
Cor do fundo: Azul claro
Alinhamento: Centro
Formato: 00
Nome: txtEstoqueMinimo *
Cor do fundo: Cinza
Alinhamento: Centro
Formato: 00
Botões de Comando
Nome: btnNovo *
Legenda: Novo
Cor da Fonte: Azul escuro
Nome: btnSalvar *
Legenda: Salvar
Cor da Fonte: Azul escuro
Nome: btnExcluir *
Legenda: Excluir
Cor da Fonte: Vermelho escuro
Como sugestão de design os campos deverão estar posicionados conforme demonstrado
na figura a seguir, assim como devem ser adicionadas as legendas dos campos, as quais
não foram descritas anteriormente:
65
Códigos do Formulário
Para que nosso formulário seja capaz de manipular os dados dos objetos produto
deveremos implementar as funcionalidades necessárias que permitam criar um novo
produto, buscar um produto já cadastrado, editar seus dados, salvá-lo ou excluí-lo.
Sendo assim criaremos algumas funções genéricas que sejam úteis para quantos
procedimentos delas necessitem, reaplicando o conceito da reutilização de código. Além
disso incluiremos os códigos dos campos que possuem eventos, além dos botões de
comando.
Todos os códigos a seguir deverão ser colocados no módulo do formulário FProduto.
1. Pesquisa de produtos: Para facilitar a busca de produtos por qualquer parte do nome
utilizaremos um campo de texto cujo valor será o elemento condicional para o código
SQL que será atribuído ao controle lstProduto, atualizando sua propriedade Origem da
Linha após cada alteração realizada no campo de pesquisa. Para isto devemos colocar o
código no evento Ao Alterar do campo txtPesquisa.
Private Sub txtPesquisa_Change()
lstProduto.RowSource = "SELECT codProduto, descricao, " & _
"unidade, valorUnitario, estoqueMinimo, qtdEstoque " & _
"FROM Produto " & _
"WHERE descricao Like '*" & txtPesquisa.Text & "*' " & _
"ORDER BY Produto.descricao;"
lstProduto.Requery
End Sub
2. Limpeza dos campos: Para efetuar a limpeza dos campos de preenchimento
utilizaremos um procedimento genérico cuja função será a de atribuir o valor nulo a
todos os campos de texto e enviar o foco ao campo txtDescricao.
Private Sub limpaCampos()
txtCodigo = Null
txtDescricao = Null
txtUnidade = Null
txtValorUnitario = Null
txtQtdEstoque = Null
txtEstoqueMinimo = Null
txtDescricao.SetFocus
End Sub
3. Novo registro: Para a criação de um novo registro em branco simplesmente iremos
chamar o procedimento limpaCampos() no evento Ao Clicar do botão btnNovo.
Private Sub btnNovo_Click()
Call limpaCampos
End Sub
66
4. Atualização de campos: Assim como fizemos um procedimento genérico que limpa
os campos para a criação de um novo registro teremos também um que preencha os
campos com as informações de um objeto produto passadas como parâmetro, ou seja, o
procedimento recebe um objeto completo e lança os valores de seus atributos nos
campos do formulário.
Private Sub atualizaCampos(argProduto As clsProduto)
txtCodigo = argProduto.codProduto
txtDescricao = argProduto.descricao
txtUnidade = argProduto.unidade
txtValorUnitario = argProduto.valorUnitario
txtQtdEstoque = argProduto.qtdEstoque
txtEstoqueMinimo = argProduto.estoqueMinimo
End Sub
5. Escolha de produto: Já que teremos também que trabalhar com produtos já
cadastrados, devemos implementar alguma maneira de recuperar suas informações.
Faremos isto utilizando a Listbox lstProduto, que utilizará o procedimento
atualizaCampos() para preencher os campos com as informações do objeto do produto
que foi clicado.
Private Sub lstProduto_Click()
Dim objProduto As New clsProduto
Dim codigoProduto As Long
codigoProduto = lstProduto.Value
If objProduto.obter(codigoProduto) Then
Call atualizaCampos(objProduto)
End If
End Sub
6. Montagem de um objeto: Esta também é uma função genérica que será utilizada por
outros procedimentos ou funções. Seu objetivo é criar um novo objeto produto, coletar
os dados digitados nos campos, atribuir ao objeto criado, e devolvê-lo para quem
chamou a função.
Private Function buscaCampos() As clsProduto
Set buscaCampos = New clsProduto
If IsNull(txtCodigo) Then
txtCodigo = proximoCodigo("codProduto", "Produto")
End If
buscaCampos.codProduto = txtCodigo
buscaCampos.descricao = txtDescricao
buscaCampos.unidade = txtUnidade
buscaCampos.valorUnitario = txtValorUnitario
buscaCampos.qtdEstoque = txtQtdEstoque
buscaCampos.estoqueMinimo = txtEstoqueMinimo
End Function
67
7. Salvando um produto: Depois de informados os dados de um novo produto, ou
alterados os dados de um produto já cadastrado, necessitaremos de um procedimento
que efetue a gravação destes dados. Para isto o botão btnSalvar conterá o código
necessário no evento Ao Clicar. Ele fará uso da função buscaCampos(), a qual
montará o objeto a ser salvo, e também o método salvar() do objeto, apresentando uma
mensagem ao usuário com o resultado da operação. Após salvar os dados de um produto
também deveremos atualizar a Listbox para que apresente estes dados, sejam eles novos
ou apenas alterados.
Private Sub btnSalvar_Click()
Dim objProduto As clsProduto
If Not IsNull(txtDescricao) And Not IsNull(txtUnidade) And _
Not IsNull(txtValorUnitario) Then
Set objProduto = buscaCampos
If objProduto.salvar Then
MsgBox "O produto foi salvo com sucesso.",vbInformation,"Salvar
produto"
lstProduto.Requery
Call atualizaCampos(objProduto)
Else
MsgBox "Ocorreu um erro durante o salvamento.", vbExclamation, _
"Salvar produto"
End If
Else
MsgBox "Informe os dados do produto.", vbExclamation, "Salvar
produto"
End If
End Sub
8. Excluindo um produto: Além de criar e alterar os dados de um produto, é
importante que também possamos excluí-lo definitivamente de nosso cadastro. Logo
devemos também implementar a funcionalidade para o botão btnExcluir, que conterá o
código necessário também no evento Ao Clicar. Ele também deverá fazer uso da função
buscaCampos() para a montagem do objeto a ser excluído, pois caso contrário a classe
não saberá que objeto deve ser excluído. Em seguida utilizaremos o método excluir() do
objeto, apresentando uma mensagem ao usuário com o resultado da operação. É claro
que após excluir um produto também deveremos atualizar a Listbox para que a mesma
reflita as alterações realizadas.
Private Sub btnExcluir_Click()
Dim objProduto As clsProduto
If Not IsNull(txtCodigo) Then
If MsgBox("Confirma a exclusão do registro?", vbQuestion +
vbYesNo, _
"Excluir produto") = vbYes Then
Set objProduto = buscaCampos
If objProduto.excluir Then
68
MsgBox "O registro foi excluído com sucesso.", vbInformation,
_
"Excluir produto"
lstProduto.Requery
Call limpaCampos
Else
MsgBox "Ocorreu um erro durante a exclusão.", vbExclamation, _
"Excluir produto"
End If
End If
End If
End Sub
Sistema de Exemplo
Novamente será disponibilizado o link para download do sistema de vendas, já
atualizado com todos os recursos do estado de desenvolvimento em que se encontrar o
projeto.
Mais uma vez recomendo aos desenvolvedores que o consultem apenas para tirar
dúvidas e realizar comparações. Aqueles que realmente quiserem aprender façam toda a
codificação e a montagem das telas manualmente. Somente assim a sua mente irá se
defrontar com as dificuldades do processo e assimilará os passos necessários para a
resolução dos problemas e a conclusão dos objetivos.
Segue o link para download:
Venda00.zip
Caso ainda não esteja com uma tela principal abra o formulário FProduto diretamente
na janela Banco de Dados (Access 2003-) ou no Painel de Navegação (Access 2007+).
Conclusão
Nesta fase não encontramos praticamente nada de novo, mas tivemos a chance de
relembrar a maioria dos procedimentos vistos até o momento. A codificação das
funcionalidades da classe Produto são muito semelhantes à codificação da classe
Cliente. Porém este era o nosso objetivo: relembrar o passo-a-passo necessário para
utilizar objetos em sistemas informatizados no MS-Access/VBA.
Com relação à parte realmente prática da série de artigos estamos chegando na reta
final. No próximo artigo, que será o último em que trabalharemos com a programação
de classes, poderemos perceber um pouco mais da dinâmica de troca de informações
entre os objetos de um sistema. Ao realizarmos vendas de produtos para clientes
estaremos englobando a utilização de todas as nossas classes de uma só vez, trabalhando
com vários objetos instanciados simultaneamente para a conclusão das tarefas.
Com toda certeza você, caro leitor que nos acompanha, já deve estar imaginando os
objetos se interagindo, os métodos realizando tarefas e retornando valores, os atributos
69
recebendo e fornecendo valores, os objetos sendo enviados inteiramente para métodos
de outros objetos, e assim sucessivamente.
É isto aí, orgulhe-se de dizer: Agora estou quase dominando essa tal de OO!
70
Classes VII - As Classes Venda e DetalheVenda
Estamos chegando na reta final da criação do sistema de vendas orientado a objetos.
Nesta etapa veremos a codificação das classes Venda e DetalheVenda, além da
implementação de um PDV que será a nossa interface gráfica para realização das
vendas.
No desenrolar da criação dos códigos com certeza você encontrará rotinas repetitivas,
funções não tão necessárias e instâncias em excesso. Além disso, poderá sentir falta de
funcionalidades importantes em um PDV ou mesmo na totalidade do sistema de vendas.
E digo mais, agora que já é praticamente um “expert” em VBA e orientação a objetos
perceberá que um ou outro método poderia ser mais bem implementado de outra
maneira. Mas antes de queimar neurônios tentando entender por que o trabalho está
assim tão “mal projetado”, lembre-se que tudo foi feito com fins didáticos, e a
repetitividade foi proposital, somente para que você fique cansado de tanto manipular
objetos e realmente aprenda como utilizá-los.
Não estaremos totalmente livres do aparecimento de “bugs”, pois apenas nos locais
estritamente necessários foram inseridas rotinas de tratamento de erros. Estes algoritmos
são cansativos de se preparar, então me dei ao luxo de não incluir em todos os
procedimentos, funções e métodos do sistema. Porém nada impede que você, que já
pesquisou o assunto e agora “saca” muito de programação, implemente ao seu bel
prazer, corrigindo erros que porventura se apresentem.
Então chega de papo e mãos à obra...
A Consulta CVenda
Deveremos criar uma consulta que será utilizada por um método da classe Venda. O
nome da consulta será CVenda e seu código SQL será o seguinte:
SELECT Venda.codVenda, Venda.codCliente, Venda.dataVenda,
DetalheVenda.codProduto,
DetalheVenda.qtdProduto, Produto.descricao, Produto.unidade,
Produto.valorUnitario,
(Produto.valorUnitario*DetalheVenda.qtdProduto) AS SubTotal
FROM Venda LEFT JOIN
(Produto RIGHT JOIN DetalheVenda ON
Produto.codProduto=DetalheVenda.codProduto)
ON Venda.codVenda=DetalheVenda.codVenda;
A Classe Venda
O objetivo desta classe, conforme dito anteriormente, é oferecer funcionalidades de
inclusão, consulta, atualização e exclusão dos objetos do tipo venda.
Este é o código da classe, com todos os seus atributos e métodos, que se utilizam de
funções próprias, bem como dos métodos dos objetos da classe ConexaoBD e da classe
Cliente.
71
Código da classe:
Option Compare Database
Option Explicit
'Atributos da Classe
'Atributo de backup e atributo identificador da Classe
'PK - Código que identifica a venda.
Private bkpCodVenda As Variant
Private lngCodVenda As Variant
'FK - Código que indica o cliente relativo à venda.
Private lngCodCliente As Variant
'Objeto da classe de Descrição de Tipo
Private objetoCliente As New clsCliente
'Data de realização da venda.
Private dtmDataVenda As Variant
'Métodos Get, Set e Let da Classe
Property Get codVenda() As Variant
codVenda = lngCodVenda
End Property
Property Let codVenda(argCodVenda As Variant)
lngCodVenda = argCodVenda
If IsEmpty(bkpCodVenda) Then
bkpCodVenda = lngCodVenda
End If
End Property
Property Get codCliente() As Variant
codCliente = lngCodCliente
End Property
Property Let codCliente(argCodCliente As Variant)
lngCodCliente = argCodCliente
End Property
Property Get dataVenda() As Variant
dataVenda = dtmDataVenda
End Property
Property Let dataVenda(argDataVenda As Variant)
dtmDataVenda = argDataVenda
End Property
Property Get objCliente() As Variant
If Not IsEmpty(codCliente) Then
If objetoCliente.obter(codCliente) Then
Set objCliente = objetoCliente
End If
End If
End Property
'Método Existe [Com conhecimento de SQL]
'Verifica a existência do objeto Venda na tabela
72
'correspondente no Banco de Dados
Function existe(argCodVenda As Variant) As Boolean
On Error GoTo Err_existe
Dim objCon As New aclConexaoBD
Dim rstExiste As Recordset
Dim strSql As String
existe = False
strSql = "Select * " & _
"From Venda " & _
"Where codVenda = " & objCon.valorSql(argCodVenda)
Set rstExiste = objCon.consulta(strSql)
If rstExiste.RecordCount > 0 Then
existe = True
End If
'Fecha o Recordset existe
rstExiste.close
Exit_existe:
Set rstExiste = Nothing
Exit Function
Err_existe:
existe = False
GoTo Exit_existe
End Function
'Método Incluir [Com conhecimento de SQL]
'Inclui um novo objeto na tabela correspondente
'dentro do Banco de dados
Function incluir() As Boolean
On Error GoTo Err_incluir
Dim objCon As New aclConexaoBD
Dim strSql As String
strSql = "Insert Into " & _
"Venda(codVenda,codCliente,dataVenda) " & _
"Values(" & objCon.valorSql(codVenda) & "," & _
objCon.valorSql(codCliente) & "," & _
objCon.valorSql(dataVenda) & ")"
incluir = (objCon.executa(strSql) > 0)
If incluir Then
'Atualiza os campos de backup
bkpCodVenda = codVenda
End If
Exit_incluir:
Exit Function
Err_incluir:
incluir = False
GoTo Exit_incluir
73
End Function
'Método Excluir [Com conhecimento de SQL]
'Exclui o objeto atual na tabela correspondente
'dentro do Banco de dados
Function excluir() As Boolean
On Error GoTo Err_excluir
Dim objCon As New aclConexaoBD
Dim strSql As String
strSql = "Delete From Venda " & _
"Where codVenda = " & objCon.valorSql(codVenda)
excluir = (objCon.executa(strSql) > 0)
Exit_excluir:
Exit Function
Err_excluir:
excluir = False
GoTo Exit_excluir
End Function
'Método Obter [Com conhecimento de SQL]
'Recupera o objeto Venda através dos argumentos informados
Function obter(argCodVenda As Variant) As Boolean
On Error GoTo Err_obter
Dim objCon As New aclConexaoBD
Dim rstObter As Recordset
Dim strSql As String
strSql = "Select * " & _
"From Venda " & _
"Where codVenda = " & objCon.valorSql(argCodVenda)
Set rstObter = objCon.consulta(strSql)
If rstObter.RecordCount = 0 Then
obter = False
Exit Function
End If
'Atualiza os campos de backup
'e os identificadores
codVenda = argCodVenda
bkpCodVenda = argCodVenda
'Atualiza os campos restantes
codCliente = rstObter.Fields("codCliente")
dataVenda = rstObter.Fields("dataVenda")
obter = True
'Fecha o Recordset obter
rstObter.close
Exit_obter:
74
Set rstObter = Nothing
Exit Function
Err_obter:
obter = False
GoTo Exit_obter
End Function
'Método Salvar [Com conhecimento de SQL]
'Salva o objeto atual na tabela correspondente
'dentro do Banco de dados
Function salvar() As Boolean
On Error GoTo Err_salvar
Dim objCon As New aclConexaoBD
Dim strSql As String
If existe(bkpCodVenda) Then
strSql = "Update Venda " & _
"Set codVenda = " & objCon.valorSql(codVenda) & _
", codCliente = " & objCon.valorSql(codCliente) & _
", dataVenda = " & objCon.valorSql(dataVenda) & _
" Where codVenda = " & objCon.valorSql(bkpCodVenda)
salvar = (objCon.executa(strSql) > 0)
Else
salvar = incluir
End If
If salvar Then
'Atualiza as variáveis de backup
'com o novo valor da chave
bkpCodVenda = codVenda
End If
Exit_salvar:
Exit Function
Err_salvar:
salvar = False
GoTo Exit_salvar
End Function
'Método getValorTotal
'Calcula a valor total da venda atual e
'devolve o valor como resultado.
Function getValorTotal() As Currency
getValorTotal = Nz(DSum("SubTotal", "CVenda", _
"codVenda=" & Nz(lngCodVenda, -1)))
End Function
'Fim da classe...
A Classe DetalheVenda
75
O objetivo desta classe, conforme dito anteriormente, é oferecer funcionalidades de
consulta e atualização dos objetos de ligação entre a venda e os produtos componentes.
Este é o código da classe, com todos os seus atributos e métodos, que se utilizam de
funções próprias, bem como dos métodos dos objetos da classe ConexaoBD e da classe
Produto.
Código da classe:
Option Compare Database
Option Explicit
'Atributos da Classe
'Atributo de backup e atributo identificador da Classe
'PK - FK - Código que indica o produto relacionado.
Private bkpCodProduto As Variant
Private lngCodProduto As Variant
'Atributo de backup e atributo identificador da Classe
'PK - FK - Código que indica a venda relacionada.
Private bkpCodVenda As Variant
Private lngCodVenda As Variant
'Quantidade do poduto relacionado.
Private dblQtdProduto As Variant
'Métodos Get, Set e Let da Classe
Property Get codProduto() As Variant
codProduto = lngCodProduto
End Property
Property Let codProduto(argCodProduto As Variant)
lngCodProduto = argCodProduto
If IsEmpty(bkpCodProduto) Then
bkpCodProduto = lngCodProduto
End If
End Property
Property Get codVenda() As Variant
codVenda = lngCodVenda
End Property
Property Let codVenda(argCodVenda As Variant)
lngCodVenda = argCodVenda
If IsEmpty(bkpCodVenda) Then
bkpCodVenda = lngCodVenda
End If
End Property
Property Get qtdProduto() As Variant
qtdProduto = dblQtdProduto
End Property
Property Let qtdProduto(argQtdProduto As Variant)
dblQtdProduto = argQtdProduto
76
End Property
'Método Existe [Com conhecimento de SQL]
'Verifica a existência do objeto DetalheVenda na
'tabela correspondente no Banco de Dados
Function existe(argCodProduto As Variant, _
argCodVenda As Variant) As Boolean
On Error GoTo Err_existe
Dim objCon As New aclConexaoBD
Dim rstExiste As Recordset
Dim strSql As String
existe = False
strSql = "Select * " & _
"From DetalheVenda " & _
"Where codProduto = " & objCon.valorSql(argCodProduto) _
And codVenda = " & objCon.valorSql(argCodVenda)"
Set rstExiste = objCon.consulta(strSql)
If rstExiste.RecordCount > 0 Then
existe = True
End If
'Fecha o Recordset existe
rstExiste.close
Exit_existe:
Set rstExiste = Nothing
Exit Function
Err_existe:
existe = False
GoTo Exit_existe
End Function
'Método Incluir [Com conhecimento de SQL]
'Inclui um novo objeto na tabela correspondente
'dentro do Banco de dados
Function incluir() As Boolean
On Error GoTo Err_incluir
Dim objCon As New aclConexaoBD
Dim strSql As String
strSql = "Insert Into " & _
"DetalheVenda(codProduto,codVenda,qtdProduto) " & _
"Values(" & objCon.valorSql(codProduto) & "," & _
objCon.valorSql(codVenda) & "," & _
objCon.valorSql(qtdProduto) & ")"
incluir = (objCon.executa(strSql) > 0)
If incluir Then
'Atualiza os campos de backup
bkpCodProduto = codProduto
bkpCodVenda = codVenda
End If
77
Exit_incluir:
Exit Function
Err_incluir:
incluir = False
GoTo Exit_incluir
End Function
'Método Excluir [Com conhecimento de SQL]
'Exclui o objeto atual na tabela correspondente
'dentro do Banco de dados
Function excluir() As Boolean
On Error GoTo Err_excluir
Dim objCon As New aclConexaoBD
Dim strSql As String
strSql = "Delete From DetalheVenda " & _
"Where codProduto = " & objCon.valorSql(codProduto) & _
" and codVenda = " & objCon.valorSql(codVenda)
excluir = (objCon.executa(strSql) > 0)
Exit_excluir:
Exit Function
Err_excluir:
excluir = False
GoTo Exit_excluir
End Function
'Método Obter [Com conhecimento de SQL]
'Recupera o objeto DetalheVenda através dos argumentos informados
Function obter(argCodProduto As Variant, _
argCodVenda As Variant) As Boolean
On Error GoTo Err_obter
Dim objCon As New aclConexaoBD
Dim rstObter As Recordset
Dim strSql As String
strSql = "Select * " & _
"From DetalheVenda " & _
"Where codProduto = " & objCon.valorSql(argCodProduto) & _
" And codVenda = " & objCon.valorSql(argCodVenda)
Set rstObter = objCon.consulta(strSql)
If rstObter.RecordCount = 0 Then
obter = False
Exit Function
End If
'Atualiza os campos de backup
'e os identificadores
codProduto = argCodProduto
bkpCodProduto = argCodProduto
codVenda = argCodVenda
78
bkpCodVenda = argCodVenda
'Atualiza os campos restantes
qtdProduto = rstObter.Fields("qtdProduto")
obter = True
'Fecha o Recordset obter
rstObter.close
Exit_obter:
Set rstObter = Nothing
Exit Function
Err_obter:
obter = False
GoTo Exit_obter
End Function
'Método Salvar [Com conhecimento de SQL]
'Salva o objeto atual na tabela correspondente
'dentro do Banco de dados
Function salvar() As Boolean
On Error GoTo Err_salvar
Dim objCon As New aclConexaoBD
Dim strSql As String
If existe(bkpCodProduto, bkpCodVenda) Then
strSql = "Update DetalheVenda " & _
"Set codProduto = " & objCon.valorSql(codProduto) & _
", codVenda = " & objCon.valorSql(codVenda) & _
", qtdProduto = " & objCon.valorSql(qtdProduto) & _
" Where codProduto = " &
objCon.valorSql(bkpCodProduto) & _
" and codVenda = " & objCon.valorSql(bkpCodVenda)
salvar = (objCon.executa(strSql) > 0)
Else
salvar = incluir
End If
If salvar Then
'Atualiza as variáveis de backup
'com o novo valor da chave
bkpCodProduto = codProduto
bkpCodVenda = codVenda
End If
Exit_salvar:
Exit Function
Err_salvar:
salvar = False
GoTo Exit_salvar
End Function
'Método getSubTotal
'Calcula o subtotal do produto vendido para a
79
'venda atual e devolve o valor como resultado
Function getSubTotal() As Currency
Dim objProduto As New clsProduto
If Not IsNull(lngCodProduto) And Not IsNull(dblQtdProduto) Then
If objProduto.obter(lngCodProduto) Then
getSubTotal = objProduto.valorUnitario * dblQtdProduto
End If
End If
End Function
'Método getListaProduto(argCodVenda As Long)
'Recebe como parâmetro um código de uma venda e devolve
'um conjunto de registros contendo os códigos dos produtos,
'além de outros dados, relativos à venda informada.
Function getListaProduto(argCodVenda As Long) As Recordset
Dim strSql As String
Dim objCon As New aclConexaoBD
strSql = "SELECT Venda.codVenda, Venda.codCliente,
Venda.dataVenda, " & _
"DetalheVenda.codProduto, DetalheVenda.qtdProduto, " & _
"Produto.descricao, Produto.unidade, Produto.valorUnitario,
" & _
"(Produto.valorUnitario * DetalheVenda.qtdProduto) AS
SubTotal " & _
"FROM Venda LEFT JOIN (Produto RIGHT JOIN DetalheVenda ON "
& _
"Produto.codProduto=DetalheVenda.codProduto) " & _
"ON Venda.codVenda=DetalheVenda.codVenda " & _
"Where Venda.codVenda=" & argCodVenda
Set getListaProduto = objCon.consulta(strSql)
End Function
'Fim da classe...
Mais Funções Auxiliares
Para exibir os itens vendidos na tela, no formato de um cupom fiscal, necessitaremos de
uma função que busque os últimos itens registrados na venda para que possamos
apresentá-los ao usuário.
Assim sendo deveremos incluir mais uma função no módulo Funcoes criado
anteriormente, que será chamada de listaProdutos() e receberá como parâmetros o
código da venda e o número de itens, a partir do último, que devem ser retornados.
Function listaProdutos(argCodVenda As Long, _
Optional ultimos As Integer = 0) As String
Dim rstLista As Recordset
Dim objDetalheVenda As New clsDetalheVenda
Dim contador
80
Set rstLista = objDetalheVenda.getListaProduto(argCodVenda)
If Not rstLista.EOF Then
rstLista.MoveLast
Else
listaProdutos = ""
Exit Function
End If
While Not rstLista.BOF And (contador < ultimos Or ultimos = 0)
listaProdutos = " " & FormatNumber(rstLista("qtdProduto"),
3) & _
" x " & FormatCurrency(rstLista("valorUnitario")) &
_
" " &
FormatCurrency(rstLista("SubTotal")) & _
vbCrLf & listaProdutos
listaProdutos = Format(rstLista("codProduto"), "000000") & "
" & _
rstLista("unidade") & " " &
rstLista("descricao") & _
vbCrLf & listaProdutos
rstLista.MovePrevious
contador = contador + 1
Wend
End Function
Repare que a função busca os itens começando do final do conjunto de registros,
inclusive montando as linhas também do final para o início, até que seja completada a
quantidade de itens solicitada, ou todos os itens caso a opção não seja informada.
Ponto Importante
Desta vez, relembrando e aprimorando os conceitos da orientação a objetos, vamos
destacar o ponto a seguir.
1. Atributo do tipo objeto: Nos atributos da classe Venda podemos encontrar um que
faz referência à classe Cliente, ou seja, o atributo é um objeto da classe Cliente. Por que
isto? Apenas adiantando um pouco o assunto do próximo artigo criaremos um modelo
de cupom fiscal (bem simples, lógico!!!) no qual esta opção será muito útil. Ao invés de
criarmos uma variável de objeto da classe Cliente e a instanciarmos bastará utilizarmos
o atributo incluído na classe Venda depois que a mesma for obtida através do código da
venda. Neste caso o objeto cliente será instanciado dentro da classe Venda
automaticamente assim que o atributo for referenciado. Com isso será possível também
acessar todos os atributos e métodos do objeto cliente. Veja os trechos de código,
primeiro o método de acesso ao atributo e depois a montagem do cupom fiscal:
Declaração da variável e método de acesso ao atributo:
'Objeto da classe de Descrição de Tipo
Private objetoCliente As New clsCliente
81
...
Property Get objCliente() As Variant
If Not IsEmpty(codCliente) Then
If objetoCliente.obter(codCliente) Then
Set objCliente = objetoCliente
End If
End If
End Property
...
Montagem do Cupom Fiscal:
cupomVenda = cupomVenda & "CPF: " & objVenda.objCliente.cpf & vbCrLf
cupomVenda = cupomVenda & "Nome: " & objVenda.objCliente.nomeCliente &
vbCrLf
A Interface Gráfica
Desta vez para que possamos visualizar e manipular os dados das vendas resolvi criar
um interessante sistema de PDV para ser a nossa interface gráfica, já que há tanta
curiosidade acerca dos mesmos.
A tela contará com exibição dos itens registrados em formato de cupom, além do
cálculo e registro de produtos utilizando o esquema de digitação do código multiplicado
pela quantidade em um único campo. Também faremos a verificação da descrição do
produto a partir do código e o cálculo de troco a partir do valor pago, além de outras
pequenas funções.
Como não poderia faltar faremos o controle de estoque, tanto baixando itens vendidos
como subindo produtos devolvidos, além de verificação de estoque baixo.
A seguir serão definidas as características para o PDV. Assim como nos anteriores as
especificações de design também são apenas sugestões, porém as informações de
origem de dados, bloqueio de campos e formatos de dados são obrigatórias, sob pena
de, novamente, não obter a funcionalidade desejada. Como de praxe as especificações
estarão marcadas com um asterisco vermelho ao lado.
No formulário teremos um rótulo que exibirá os produtos vendidos, alguns campos de
texto para digitação das informações e os botões de comando para criar e cancelar
vendas, excluir produtos e fechar a tela do PDV.
Obs: Somente as propriedades que foram alteradas para um valor diferente do padrão
serão apresentadas. Para as demais utilize o padrão do sistema, ou o valor que desejar,
desde que não comprometa a funcionalidade do sistema.
Formulário
Nome: FVenda
Largura: 22cm
Altura: Cabeçalho (1,6cm) / Detalhe (15cm)
82
Cor do fundo: Cabeçalho (Vermelho) / Detalhe (Azul escuro)
Legenda: Sistema de Vendas
Estilo da borda: Fino
Seletores de registro: Não
Botões de navegação: Não
Linhas divisórias: Não
Barras de rolagem: Nenhuma
Caixa de controle: Não
Botão Fechar: Não
Botões Min Max: Nenhum
Popup: Sim
Janela Restrita: Não
Controle Label
Nome: lblProdutos *
Largura: 9,7cm *
Altura: 10,4cm *
Controles Textbox
Nome: txtCodigoVenda *
Formato: 000000
Ativado: Não *
Bloqueado: Sim *
Cor do fundo: Cinza
Alinhamento: Centro
Nome: txtData *
Formato: dd/mm/yyyy *
Cor do fundo: Branco
Máscara de entrada: 00/00/00;;_
Alinhamento: Centro
Nome: txtCodCliente *
Formato: 000
Cor do fundo: Branco
Alinhamento: Centro
Nome: txtNomeCliente *
Cor do fundo: Cinza
Alinhamento: Esquerda
Ativado: Não *
Bloqueado: Sim *
Nome: txtProdutoQtd *
Cor do fundo: Amarelo
Alinhamento: Esquerda
Tamanho da Fonte: 28
Nome: txtDescricao *
Cor do fundo: Cinza
Alinhamento: Esquerda
Tamanho da Fonte: 14
Ativado: Não *
Bloqueado: Sim *
Nome: txtUnidade *
Cor do fundo: Cinza
Alinhamento: Centro
83
Tamanho da Fonte: 14
Ativado: Não *
Bloqueado: Sim *
Nome: txtQtd *
Cor do fundo: Cinza
Formato: Padrão
Casas decimais: 3
Alinhamento: Centro
Tamanho da Fonte: 14
Ativado: Não *
Bloqueado: Sim *
Nome: txtValorUnt *
Cor do fundo: Cinza
Formato: Unidade Monetária
Alinhamento: Direita
Tamanho da Fonte: 14
Ativado: Não *
Bloqueado: Sim *
Nome: txtSubTotal *
Cor do fundo: Cinza
Formato: Unidade Monetária
Alinhamento: Direita
Tamanho da Fonte: 14
Ativado: Não *
Bloqueado: Sim *
Nome: txtTotal *
Cor do fundo: Laranja
Formato: Unidade Monetária
Alinhamento: Direita
Tamanho da Fonte: 24
Ativado: Não *
Bloqueado: Sim *
Nome: txtPago *
Cor do fundo: Cinza
Formato: Unidade Monetária
Alinhamento: Direita
Tamanho da Fonte: 16
Nome: txtTroco *
Cor do fundo: Cinza
Formato: Unidade Monetária
Alinhamento: Direita
Tamanho da Fonte: 16
Ativado: Não *
Bloqueado: Sim *
Botões de Comando
Nome: btnExcluirProduto *
Legenda: Excluir Produto
Cor da Fonte: Vermelho escuro
Nome: btnCancelar *
Legenda: Cancelar Venda
Cor da Fonte: Vermelho escuro
84
Nome: btnNovo *
Legenda: Nova Venda
Cor da Fonte: Azul escuro
Nome: btnFechar *
Legenda: Fechar
Cor da Fonte: Vermelho escuro
Como sugestão de design os campos deverão estar posicionados conforme demonstrado
na figura a seguir, assim como devem ser adicionadas as legendas dos campos, as quais
não foram descritas anteriormente:
Códigos do Formulário
Para que nosso formulário seja capaz de manipular os dados dos objetos produto
deveremos implementar as funcionalidades necessárias que permitam criar uma nova
venda, calcular os valores dos produtos vendidos, registrar as vendas, controlar estoque,
devolver produtos e cancelar a venda.
Sendo assim criaremos algumas funções genéricas que sejam úteis para quantos
procedimentos delas necessitem, reaplicando o conceito da reutilização de código. Além
disso, incluiremos os códigos dos campos que possuem eventos, assim como dos botões
de comando.
Todos os códigos a seguir deverão ser colocados no módulo do formulário FVenda.
85
1. Limpeza dos campos: Para efetuar a limpeza dos campos de preenchimento
utilizaremos dois procedimentos genérico cuja função será a de atribuir o valor nulo a
todos os campos de texto, deixar o rótulo de exibição do cupom em branco e enviar o
foco ao campo txtProdutoQtd. Repare que um procedimento reutiliza o código do
outro, já que os dois são independentes e o primeiro é utilizado apenas na descrição do
produto e o segundo limpa todos os campos da tela.
Private Sub limpaExibicaoProduto()
txtDescricao = Null
txtUnidade = Null
txtQtd = Null
txtValorUnt = Null
txtSubtotal = Null
End Sub
Private Sub limpaCampos()
txtCodigoVenda = Null
txtData = Null
txtCodCliente = Null
txtNomeCliente = Null
txtProdutoQtd = Null
txtTotal = Null
txtPago = Null
txtTroco = Null
Call limpaExibicaoProduto
lblProdutos.Caption = ""
txtProdutoQtd.SetFocus
End Sub
2. Nova venda: Para a criação de uma nova venda em branco simplesmente iremos
chamar o procedimento limpaCampos() no evento Ao Clicar do botão btnNovo.
Private Sub btnNovo_Click()
Call limpaCampos
End Sub
3. Cálculo do troco: Com a finalidade de informar o valor do troco a partir do valor da
venda e do valor pago utilizaremos um procedimento que será acionado no evento Após
Atualizar do campo txtPago.
Private Sub txtPago_AfterUpdate()
If Not IsNull(txtPago) Then
txtTroco = CDbl(txtPago) - CDbl(Nz(txtTotal))
Else
txtTroco = Null
End If
End Sub
4. Exibição da lista de produtos registrados: Para exibir a lista de produtos
registrados sempre que for incluído ou retirado algum produto utilizaremos um
procedimento genérico cuja função será a de buscar o lista utilizando a função
listaProdutos(), atualizar a exibição do valor total da venda no campo txtTotal e
acionar o procedimento que calcula e exibe o valor do troco.
Sub atualizaLista()
86
Dim objVenda As New clsVenda
If Not IsNull(txtCodigoVenda) Then
If objVenda.obter(txtCodigoVenda) Then
lblProdutos.Caption = listaProdutos(Nz(txtCodigoVenda, -1), 10)
txtTotal = objVenda.getValorTotal
Call txtPago_AfterUpdate
End If
End If
End Sub
5. Carregamento do formulário: No evento Ao Carregar do formulário
implementaremos uma pequena rotina que simplesmente atualizará a lista de produtos e
moverá o foco para o campo txtProdutoQtd. A atualização da lista só fará sentido
quando abrirmos novamente um formulário já carregado anteriormente (por exemplo,
quando abrimos o formulário, mudamos para o modo Design e voltamos para o modo
Formulário).
Private Sub Form_Load()
Call atualizaLista
txtProdutoQtd.SetFocus
End Sub
6. Criação da venda: Ao iniciarmos uma venda deveremos gerar o código da mesma.
Assim criaremos um procedimento genérico que lança mão da função
proximoCodigo() para gerar o código da venda, além de atribuir a data atual caso o
usuário não tenha informado alguma data específica.
Sub preencheVenda()
If IsNull(txtCodigoVenda) Then
txtCodigoVenda = proximoCodigo("codVenda", "Venda")
End If
If IsNull(txtData) Then
txtData = Date
End If
End Sub
7. Busca do cliente: Para que seja exibido o nome do cliente assim que o código for
informado utilizaremos esta rotina no evento Após Atualizar do campo txtCodCliente.
Assim será preenchido o campo txtNomeCliente a partir dos dados retornados pelo
objeto cliente, ou então um aviso ao usuário informando que o código do cliente é
inválido. Ao ser informado um cliente válido a venda também será gerada através de
uma chamada ao procedimento preencheVenda().
Private Sub txtCodCliente_AfterUpdate()
Dim objCliente As New clsCliente
If Not IsNull(txtCodCliente) Then
If objCliente.obter(CLng(txtCodCliente)) Then
Call preencheVenda
txtNomeCliente = objCliente.nomeCliente
87
Else
txtNomeCliente = Null
txtCodCliente = Null
MsgBox "Código do cliente inválido!", _
vbExclamation, "Erro!"
End If
End If
End Sub
8. Busca do produto em tempo real: O procedimento a seguir permitirá que seja
exibida a descrição e a unidade do produto no exato momento em que o código for
digitado. Ele será incluído no evento Ao Alterar do campo txtProdutoQtd. Caso o
código não corresponda a nenhum produto será exibida uma mensagem no campo
txtDescricao informando que o código não está cadastrado. Veja que ele somente será
executado completamente caso o valor digitado possa ser interpretado como valor
numérico, ou seja, um valor compatível com o código do produto.
Private Sub txtProdutoQtd_Change()
Dim objProduto As New clsProduto
If txtProdutoQtd.Text <> "" Then
If InStr(txtProdutoQtd.Text, "*") = 0 Then
If IsNumeric(txtProdutoQtd.Text) Then
If objProduto.obter(CLng(txtProdutoQtd.Text)) Then
txtDescricao = objProduto.descricao
txtUnidade = objProduto.unidade
Else
txtDescricao = "Código não cadastrado..."
txtUnidade = Null
End If
txtQtd = Null
txtValorUnt = Null
txtSubtotal = Null
End If
End If
Else
txtDescricao = Null
txtUnidade = Null
End If
End Sub
9. Exibição de produto: Para exibirmos todos os dados do produto a ser registrado em
uma venda criaremos um procedimento genérico que recebe como argumento um objeto
detalhe de venda cujas propriedades e métodos fornecerão as informações para o
preenchimento dos campos. Repare que o valor do subtotal é calculado pelo método
getSubTotal() do objeto.
Sub exibeProduto(argDetalhe As clsDetalheVenda)
Dim objProduto As New clsProduto
If objProduto.obter(argDetalhe.codProduto) Then
txtDescricao = objProduto.descricao
txtUnidade = objProduto.unidade
txtQtd = argDetalhe.qtdProduto
88
txtValorUnt = objProduto.valorUnitario
txtSubtotal = argDetalhe.getSubTotal
End If
End Sub
10. Registro do produto: Cada produto constante da venda deverá ser registrado antes
do fechamento da venda. Para isto deveremos realizar várias verificações, utilizando
vários objetos neste procedimento que será incluído no evento Antes de Atualizar do
campo txtProdutoQtd para que seja capaz de cancelar o registro caso alguma
informação não esteja correta. Primeiro será verificada a validade do código do produto
e a correta informação da quantidade, caso seja incluída, atribuindo uma unidade em
caso contrário. Em seguida será checada a validade do código da venda e também da
data, além do código do cliente, caso seja informado (uma venda sem cliente será
considerada venda ao consumidor). Para não ocorrerem conflitos o código da venda
será sempre gerado automaticamente, não sendo permitida sua alteração posterior. O
próximo passo será verificar se a quantidade solicitada existe em estoque. Caso não
possa ser atendida integralmente o registro é cancelado. Existindo a quantidade o
produto é registrado, assim como qualquer alteração nos dados da venda. Por último a
exibição dos dados é atualizada e o estoque atualizado, informando o usuário se o
estoque ficar abaixo do mínimo. Caso ocorram erros durante o processo o esquema de
tratamento de erros avisa o usuário sobre qual passo gerou o problema.
Private Sub txtProdutoQtd_BeforeUpdate(Cancel As Integer)
On Error GoTo Err_txtProdutoQtd_AfterUpdate
Dim codigoProduto As Long
Dim qtdProduto As Double
Dim posicao As Integer
Dim status As Integer
Dim objVenda As New clsVenda
Dim objDetalhe As New clsDetalheVenda
Dim objProduto As New clsProduto
status = 0
If Not IsNull(txtProdutoQtd) Then
Call preencheVenda
posicao = InStr(txtProdutoQtd, "*")
If posicao > 0 Then
status = 1
codigoProduto = CLng(Left(txtProdutoQtd, posicao - 1))
status = 2
qtdProduto = CDbl(Right(txtProdutoQtd, Len(txtProdutoQtd) -
posicao))
Else
status = 3
codigoProduto = CLng(txtProdutoQtd)
qtdProduto = 1
End If
status = 4
objVenda.codVenda = CLng(txtCodigoVenda)
status = 5
89
objVenda.dataVenda = CDate(txtData)
status = 6
If Not IsNull(txtCodCliente) Then
objVenda.codCliente = CLng(txtCodCliente)
End If
If objProduto.obter(codigoProduto) Then
If objProduto.qtdEstoque < qtdProduto Then
MsgBox "O estoque existente não é suficiente!" & vbCrLf & vbLf
& _
"Estoque atual: " & FormatNumber(objProduto.qtdEstoque,
3) & _
" " & objProduto.unidade, _
vbExclamation, "Estoque Baixo"
Cancel = True
Exit Sub
End If
End If
status = 7
If objVenda.salvar Then
objDetalhe.codVenda = objVenda.codVenda
objDetalhe.codProduto = codigoProduto
objDetalhe.qtdProduto = qtdProduto
status = 8
If objDetalhe.salvar Then
Call exibeProduto(objDetalhe)
Call atualizaLista
If objProduto.obter(codigoProduto) Then
status = 9
If objProduto.baixarEstoque(qtdProduto) Then
If objProduto.estoqueBaixo Then
MsgBox "O estoque ficou abaixo da quantidade mínima!" &
_
vbCrLf & vbLf & _
"Estoque atual: " &
FormatNumber(objProduto.qtdEstoque, 3) & _
" " & objProduto.unidade, vbExclamation, "Estoque
Baixo"
End If
Else
MsgBox "Ocorreu um erro na atualização do estoque.", _
vbExclamation, "Erro!"
End If
End If
Else
MsgBox "Ocorreu um erro ao incluir o produto.", _
vbExclamation, "Erro!"
Cancel = True
End If
Else
MsgBox "Ocorreu um erro ao incluir a venda.", _
vbExclamation, "Erro!"
Cancel = True
End If
End If
Exit_txtProdutoQtd_AfterUpdate:
90
Exit Sub
Err_txtProdutoQtd_AfterUpdate:
Select Case status
Case 1, 3
MsgBox "Código do produto inválido!", vbExclamation, "Erro!"
Case 2
MsgBox "Quantidade do produto inválida!", vbExclamation, "Erro!"
Case 5
MsgBox "Data inválida!", vbExclamation, "Erro!"
Case 6
MsgBox "Código do cliente inválido!", vbExclamation, "Erro!"
Case 7
MsgBox "Ocorreu um erro ao incluir a venda!", vbExclamation,
"Erro!"
Case 8
MsgBox "Ocorreu um erro ao incluir o produto!", vbExclamation,
"Erro!"
Case 9
MsgBox "Ocorreu um erro ao atualizar o estoque!", vbExclamation,
"Erro!"
Case Else
MsgBox "Ocorreu um erro. O sistema informou a seguinte
mensagem:" & _
vbCrLf & vbLf & Err.Description, vbExclamation, "Erro!"
End Select
Cancel = True
Resume Exit_txtProdutoQtd_AfterUpdate
End Sub
Obs: Apenas para esclarecimento sobre o tratamento de erros lembre-se que este não é
um sistema real. Sendo assim os códigos foram colocados de qualquer maneira para
atingir o objetivo do curso: aprender a utilizar os objetos. Da maneira que foram
implementados os registros um erro no meio do processo impediria que o final do
registro de uma venda ou de um produto ocorresse, deixando os dados corrompidos.
Em um sistema real deveriam ser utilizadas transações para garantir que todos os
processos ocorressem integralmente, do início ao fim, ou então que todas as etapas
fossem canceladas e os dados retornassem ao estado original. Não poderíamos, por
exemplo, ter uma venda registrada sem que o estoque fosse atualizado. Mas isto é outra
história...
11. Atualização das informações: Após o registro de um produto as informações
exibidas devem ser atualizadas e o foco retornar para o campo de digitação de produto e
quantidade, a fim de que ele esteja pronto para efetuar novo registro. Para executar esta
tarefa criaremos um procedimento no evento Após Atualizar do campo
txtProdutoQtd.
Private Sub txtProdutoQtd_AfterUpdate()
btnNovo.SetFocus
txtProdutoQtd.SetFocus
txtProdutoQtd = Null
Call preencheVenda
Call atualizaLista
End Sub
91
12. Exclusão de produto da venda: Caso tenhamos que excluir um único produto da
venda, antes que ela seja fechada, deveremos desfazer os passos executados no registro
do mesmo. Para isto devemos criar um procedimento no evento Ao Clicar do botão
btnExcluirProduto. Ele será encarregado de solicitar ao usuário o código do produto
que deverá ser excluído, checando sua validade, atualizando o estoque e finalmente
excluindo o produto, sendo que os dados de exibição devem ser novamente atualizados
através de uma chamada ao procedimento genérico atualizaLista(). Repare que não
excluiremos o produto em si, mas apenas o seu registro na venda.
Private Sub btnExcluirProduto_Click()
Dim objDetalheVenda As New clsDetalheVenda
Dim objProduto As New clsProduto
Dim codigoProduto As String
If Not IsNull(txtCodigoVenda) Then
codigoProduto = InputBox("Informe o código do produto a ser
excluído:", _
"Exclusão de Produto")
Else
Exit Sub
End If
If codigoProduto <> "" Then
If IsNumeric(codigoProduto) Then
If objDetalheVenda.obter(CLng(codigoProduto),
CLng(txtCodigoVenda)) Then
If objProduto.obter(CLng(codigoProduto)) Then
If objProduto.subirEstoque(objDetalheVenda.qtdProduto) Then
If objDetalheVenda.excluir Then
MsgBox "O produto foi excluído com sucesso!", _
vbInformation, "Exclusão de Produto"
Call atualizaLista
Else
MsgBox "Ocorreu um erro durante a exclusão do produto!",
_
vbExclamation, "Exclusão de Produto"
End If
End If
End If
End If
Else
MsgBox "Código de produto inválido!", _
vbExclamation, "Exclusão de Produto"
End If
End If
End Sub
Obs: Veja que não incluí tratamento de erro neste procedimento. Também não incluí e
nem incluirei em vários outros. Esta tarefa deverá ser executada no evento Ao Praticar
do objeto LeitorAplicado.
13. Cancelamento da venda: Assim como podemos excluir um produto registrado
também podemos cancelar uma venda inteira. Para executar esta tarefa deveremos
desfazer todos os passos executados para registrar todos os produtos até o momento. O
procedimento responsável será incluído no evento Ao Clicar do botão btnCancelar. O
92
cancelamento deverá contemplar a busca do conjunto de registros dos produtos da
venda, através do método getListaProduto() do objeto detalhe de venda, que serão
verificados um a um para retorno ao estoque. Em seguida a venda será excluída,
ocasionando a exclusão dos registros associados na tabela DetalheVenda pelo
mecanismo do relacionamento em cascata. Após a exclusão os campos serão limpos
para iniciar nova venda.
Private Sub btnCancelar_Click()
Dim objVenda As New clsVenda
Dim objDetalheVenda As New clsDetalheVenda
Dim objProduto As New clsProduto
Dim rstLista As Recordset
If objVenda.obter(Nz(txtCodigoVenda, -1)) Then
If MsgBox("Confirma o cancelamento da venda?", _
vbQuestion + vbYesNo, "Cancelar Venda") = vbYes Then
Set rstLista = objDetalheVenda.getListaProduto(txtCodigoVenda)
While Not rstLista.EOF
Call objProduto.obter(rstLista("codProduto"))
Call objProduto.subirEstoque(rstLista("qtdProduto"))
rstLista.MoveNext
Wend
If objVenda.excluir Then
MsgBox "A venda foi cancelada com sucesso.", _
vbInformation, "Cancelar Venda"
Call limpaCampos
Else
MsgBox "Ocorreu um erro durante o cancelamento.", _
vbExclamation, "Cancelar Venda"
End If
End If
End If
End Sub
14. Fechamento do PDV: Para encerrarmos as atividades no PDV simplesmente
iremos incluir um procedimento que fecha o formulário no evento Ao Clicar do botão
btnFechar.
Private Sub btnFechar_Click()
DoCmd.close
End Sub
Sistema de Exemplo
Novamente será disponibilizado o link para download do sistema de vendas, já
atualizado com todos os recursos do estado de desenvolvimento em que se encontrar o
projeto.
93
Continua a recomendação de consultar o exemplo apenas para tirar dúvidas e verificar
como foi feito, além de ver o sistema em funcionamento. Treinem bastante, digitem,
testem, façam o cérebro pedir água. Esta é a única maneira de realmente assimilar o
conhecimento.
Segue o link para download:
Venda00.zip
Caso ainda não esteja com uma tela principal abra o formulário FVenda diretamente na
janela Banco de Dados (Access 2003-) ou no Painel de Navegação (Access 2007+).
Conclusão
Depois desta exaustiva etapa foi possível percebermos o quanto os objetos devem
interagir para chegar ao fim de uma tarefa. Funções, procedimentos, eventos, métodos e
atributos trabalham em conjunto para produzir resultados que atendam as necessidades
do usuário.
Este artigo foi dedicado a demonstrar de maneira maçante a criação, utilização e troca
de informações entre objetos em meio aos códigos do restante do sistema, por isso
mesmo foi bem mais longo que os demais.
Além de toda a programação em si foi possível trabalharmos apresentando um sistema
interessante, útil e muito comum no dia a dia, do qual muitos tinham a curiosidade de
conhecer e compreender a forma de implementação: o PDV. Mas nosso sistema de
vendas ultramoderno, com tecnologia orientada a objetos, não estaria completo sem ele,
não é mesmo?
Aqui praticamente encerramos a parte mais trabalhosa. De agora em diante veremos
apenas a finalização do sistema, algo mais estético do que funcional, além de um pouco
mais teoria nos artigos seguintes, com a apresentação do Genesis, a ferramenta case
para VBA que facilitará a sua vida, e a abordagem sobre utilização de objetos no MS-
Access para aprimorar seus projetos.
94
Classes VIII - Finalização do Sistema
Eis que chegamos à última etapa da criação do nosso sistema de vendas orientado a
objetos.
Finalmente teremos o sistema totalmente pronto e funcional para que seja possível
visualizar a aplicação de todos os conceitos aprendidos nesta jornada de busca do
conhecimento.
A finalização do aplicativo não trará exatamente alguma novidade, mas apenas o
fechamento do projeto, ao criarmos a interface gráfica de gerenciamento principal do
software e os relatórios para exibição dos dados. A exceção será para a apresentação do
cupom de venda, que será um exemplo bastante simplificado de um cupom fiscal de
verdade, utilizado em sistemas comerciais.
A Última Função Auxiliar
Para montar o cupom de venda, no formato de um cupom fiscal, necessitaremos de uma
função que busque a lista de produtos e acrescente os dados da empresa ou do sistema,
além dos dados do cliente, caso haja algum específico.
Assim sendo deveremos incluir mais uma função no módulo Funcoes criado
anteriormente, que será chamada de cupomVenda(), a qual utilizará a função
listaProdutos() criada anteriormente. A função cupomVenda() receberá como
parâmetro o código da venda para a qual o cupom deve ser retornado.
Este será o código da função:
Function cupomVenda(argCodVenda As Long) As String
Dim objVenda As New clsVenda
If Not objVenda.obter(argCodVenda) Then
cupomVenda = ""
Exit Function
End If
cupomVenda = "VENDA OO" & vbCrLf
cupomVenda = cupomVenda & "Sistema de Venda Orientado a Objetos" & vbCrLf
cupomVenda = cupomVenda & "Av. do Passa 4 nr 171, Centro" & vbCrLf
cupomVenda = cupomVenda & "Objetolândia - OB" & vbCrLf
cupomVenda = cupomVenda & "Fone: (0xx62) 3322-4455" & vbCrLf
cupomVenda = cupomVenda & "++++++++++++++++++++++++++++++++++++++++" &
vbCrLf
cupomVenda = cupomVenda & Format(objVenda.dataVenda, "dd/mm/yy") & _
" Venda: " & _
Format(objVenda.codVenda, "000000") & vbCrLf
cupomVenda = cupomVenda & vbCrLf
cupomVenda = cupomVenda & " CUPOM FISCAL" & vbCrLf
cupomVenda = cupomVenda & vbCrLf
cupomVenda = cupomVenda & listaProdutos(argCodVenda)
cupomVenda = cupomVenda & "++++++++++++++++++++++++++++++++++++++++" &
vbCrLf
95
cupomVenda = cupomVenda & vbCrLf
cupomVenda = cupomVenda & "TOTAL " &
_
FormatCurrency(objVenda.getValorTotal) & vbCrLf
cupomVenda = cupomVenda & vbCrLf
If Not IsNull(objVenda.codCliente) Then
cupomVenda = cupomVenda & "Cliente: " &
Format(objVenda.objCliente.codCliente, _
"0000") & vbCrLf
cupomVenda = cupomVenda & "CPF: " & objVenda.objCliente.cpf & vbCrLf
cupomVenda = cupomVenda & "Nome: " & objVenda.objCliente.nomeCliente &
vbCrLf
Else
cupomVenda = cupomVenda & "Venda ao consumidor" & vbCrLf
End If
cupomVenda = cupomVenda & vbCrLf
cupomVenda = cupomVenda & "Obrigado e volte Sempre..."
End Function
Veja que no caso de não haver um cliente registrado para a venda ela será exibida como
Venda ao consumidor, já que nem sempre em uma venda é necessário identificar o
cliente.
Pronto. Agora, a partir de qualquer local do nosso projeto, poderemos buscar um cupom
de venda apenas utilizando a função e passando o código da venda como parâmetro.
O Formulário Principal
O formulário principal é o que se abre automaticamente assim que o aplicativo é
iniciado e será o responsável por permitir acesso aos demais itens do sistema.
A seguir serão definidas as características para o formulário principal. Assim como nos
anteriores as especificações de design também são apenas sugestões, porém as
informações de origem de dados, bloqueio de campos e formatos de dados são
obrigatórias, sob pena de, novamente, não obter a funcionalidade desejada. Como de
praxe as especificações estarão marcadas com um asterisco vermelho ao lado.
No formulário teremos apenas os rótulos como o nome e a descrição do sistema, além
de mais seis botões de comando para acesso aos formulários de cadastro, PDV e
relatórios, com seus respectivos rótulos.
Formulário
Nome: FPrincipal
Altura: Cabeçalho (4cm) / Detalhe (11cm)
Cor do fundo: Cabeçalho (Azul) / Detalhe (Cinza claro)
Legenda: Sistema de Vendas
Seletores de registro: Não
Botões de navegação: Não
Linhas divisórias: Não
Barras de rolagem: Nenhuma
Caixa de controle: Sim
Botão Fechar: Sim
Botões Min Max: Nenhum
96
Queremos que nosso formulário seja exibido automaticamente assim que o Sistema de
Vendas for aberto. Também não queremos que o painel de navegação (ou a Janela
Banco de Dados) seja exibido. Para isso vamos realizar o seguinte procedimento:
No Access 2003 ou inferior: Vá ao menu Ferramentas -> Inicializar. Em seguida, na
caixa de texto Exibir Formulário/Página informe o nome do formulário FPrincipal.
Desmarque a opção Exibir Janela Banco de Dados e clique em Ok.
No Access 2007: Vá ao Botão do Office, no canto superior esquerdo da tela, depois em
Opções do Access -> Banco de Dados Atual. Em seguida, na caixa de texto Exibir
Formulário informe o nome do formulário FPrincipal. Desmarque também a opção
Exibir Painel de Navegação e clique em Ok.
No Access 2010: Vá ao menu Arquivo, depois em Opções -> Banco de Dados Atual.
Em seguida, na caixa de texto Exibir Formulário informe o nome do formulário
FPrincipal.
Botões de Comando
Nome: btnCliente *
Nome: btnProduto *
Nome: btnPDV *
Nome: btnListaCliente *
Nome: btnListaProduto *
Nome: btnRelVenda *
Como sugestão de design os campos deverão estar posicionados conforme demonstrado
na figura a seguir, assim como devem ser adicionadas as legendas dos botões, as quais
não foram descritas anteriormente:
97
Códigos do Formulário
Para que nosso formulário seja capaz de permitir o acesso aos itens do sistema
deveremos programar as funcionalidades necessárias que permitam abrir os formulários
e relatórios existentes.
Por ser um processo bastante simples e repetitivo não detalharei cada um dos comandos.
Colocarei os comandos agrupados pelo seu tipo de funcionalidade.
Todos os códigos a seguir deverão ser colocados no módulo do formulário FPrincipal.
1. Abertura dos formulários de cadastro: Para abrirmos os formulários de cadastro e
o PDV utilizaremos o evento Ao Clicar dos botões com o comando OpenForm.
Private Sub btnCliente_Click()
DoCmd.OpenForm "FCliente"
End Sub
Private Sub btnPDV_Click()
DoCmd.OpenForm "FVenda"
End Sub
Private Sub btnProduto_Click()
DoCmd.OpenForm "FProduto"
End Sub
2. Abertura dos relatórios: Para abrirmos os relatórios utilizaremos o evento Ao
Clicar dos botões com o comando OpenReport.
98
Private Sub btnListaCliente_Click()
DoCmd.OpenReport "RCliente", acViewPreview
End Sub
Private Sub btnListaProduto_Click()
DoCmd.OpenReport "RProduto", acViewPreview
End Sub
Private Sub btnRelVenda_Click()
DoCmd.OpenReport "RVenda", acViewPreview
End Sub
3. Maximização do formulário: Para que o formulário principal se mantenha
maximizado o tempo todo utilizaremos o comando Maximize nos eventos Ao Abrir e
Ao Ativar do mesmo.
Private Sub Form_Activate()
DoCmd.Maximize
End Sub
Private Sub Form_Open(Cancel As Integer)
DoCmd.Maximize
End Sub
Relatórios
Os dados cadastrados e as vendas registradas poderão ser exibidos e preparados para
impressão através dos relatórios que criaremos.
Como não é o objetivo deste curso não detalharemos a criação dos relatórios, sendo que
apenas descreveremos a função de cada um deles. Os relatórios Lista de Clientes e
Lista de Produtos devem ser gerados de maneira simples a partir das tabelas Cliente e
Produto, respectivamente. O Relatório de Vendas deve ser gerado a partir da consulta
CVenda. Já o cupom fiscal será um pouco mais detalhado, já que utiliza um controle
não acoplado com a função cupomVenda().
Lista de Clientes
Responsável por exibir os dados dos clientes cadastrados em ordem alfabética de
nomes. O nome dele será RCliente.
Lista de Produtos
Responsável por exibir os dados dos produtos cadastrados em ordem alfabética de
descrição. O nome dele será RProduto.
99
Relatório de Vendas
Responsável por exibir os dados das vendas registradas, o nome dos clientes e os
produtos componentes de cada venda. O nome dele será RVenda.
Cupom de Venda
O cupom de venda exibirá os dados da venda em formato de um cupom fiscal
simplificado. O nome dele será RCupom.
Ao invés do formulário principal ele será acionado a partir do PDV, ao clicarmos duas
vezes no controle lblProdutos, o mesmo que exibe os produtos registrados no momento
da venda no PDV. Para isto incluiremos o código abaixo no evento Ao Clicar Duas
Vezes no controle lblProdutos do formulário FVenda.
Private Sub lblProdutos_DblClick(Cancel As Integer)
If Not IsNull(txtCodigoVenda) Then
DoCmd.OpenReport "RCupom", acViewPreview, , , , txtCodigoVenda
End If
End Sub
100
Repare que o código da venda, que será o parâmetro para a função cupomVenda(), será
passado através do argumento OpenArgs de abertura do relatório. Ao abrir o formulário
o código estará disponível para a função através da propriedade AbrirArgs do relatório.
Para montarmos o relatório com o cupom criaremos apenas um controle caixa de texto
não acoplado com o seguinte código:
=cupomVenda(Nz([AbrirArgs]))
A função buscará todas as informações sobre a venda e seus produtos e retornará para o
controle, que as exibirá no relatório.
O controle terá o nome de txtCupom, a propriedade Pode Ampliar definida como Sim
e a largura de 14 cm.
O relatório deverá ter sua propriedade Popup definida como Sim.
Veja abaixo como será o modelo do cupom de venda no modo de visualização:
Sistema de Exemplo
Novamente será disponibilizado o link para download do sistema de vendas, desta vez
finalizado e totalmente funcional.
101
Continua a recomendação de consultar o exemplo apenas para tirar dúvidas e verificar
como foi feito, além de ver o sistema em funcionamento. O treinamento ainda é a
maneira recomendada de aprender.
Segue o link para download
Venda00.zip
A fim de auxiliar na resolução de problemas enfrentados pelos leitores que tentam
colocar em prática os ensinamentos deixo aqui a informação sobre quais referências
estão marcadas no editor do VBA. Para isto veja a imagem a seguir.
A versão utilizada para montar o exemplo foi o Access 2007, sendo que salvei o mesmo
no formato do Access 2000, para que todos pudessem acessá-lo normalmente.
Conclusão
Após alguns meses de suado trabalho, de minha parte, e bastante estudo e treinamento,
da parte de vocês leitores, conseguimos finalizar nosso projeto, criando o sistema de
vendas orientado a objetos.
Além da utilização massiva de objetos para desempenhar as funções de cadastro,
cálculo, validação e acesso a dados, aprendemos como criar um PDV simples e também
um modelo de cupom de venda, também bastante simples.
102
Depois de toda esta cansativa rotina de programação, no caso da digitação de todos
aqueles códigos das classes, não seria maravilhoso que esta tarefa pudesse ser
simplificada? Não seria ótimo que não tivéssemos que nos preocupar com a montagem
dos códigos SQL dos métodos das classes, nem com os métodos de acesso às
propriedades? E quando alteramos um atributo da classe, será que temos que refazer
tudo de novo?
Pois bem, este será o assunto do próximo artigo, no qual será apresentada a ferramenta
Genesis, um aplicativo que cria as classes para o nosso projeto. Você apenas informa
quais são os campos das tabelas e sua descrição, então o Genesis cria o banco de dados
e as classes em um piscar de olhos. Alterou alguma coisa? Basta gerar novamente a
classe e importar para seu projeto outra vez. Simples assim.
Além da criação das classes com o Genesis, veremos dicas de como agilizar a
importação e a remoção de módulos de nosso projeto, a fim de evitar que tenhamos que
importar ou remover um por um, o que já nos trás um grande aumento de produtividade.
103
Classes IX - Genesis - A Ferramenta Case
Frequentemente nós, profissionais e amadores da área de informática, nos deparamos
com algumas tarefas extremamente chatas existentes no desenvolvimento de sistemas,
algumas delas relacionadas aos trabalhos repetitivos e cansativos, como, por exemplo, a
documentação de atributos e a programação de métodos básicos de classes, ou seja,
aqueles que atribuem ou retornam valores, buscam, salvam ou excluem um objeto, entre
outros.
Segundo criadores de sistemas análogos, o trabalho de programação pode ser reduzido,
em média, até 60%, ao se utilizar os geradores de código. Isto permite que o
profissional concentre-se apenas em implementar os métodos inerentes às regras do
negócio, diminuindo consideravelmente o tempo necessário para a conclusão do projeto.
Algumas das ferramentas de geração de código existentes mantém o seu foco na criação
de classes a partir de um Banco de Dados pronto, conectando-se ao mesmo e buscando
as informações sobre a estrutura das tabelas e relacionamentos. Outras geram as classes
a partir dos modelos de projeto. Existem também algumas especializadas em modelar o
próprio esquema do Banco de Dados, fornecendo os script´s SQL para a criação do
mesmo. Umas mais simples, outras nem tanto, a verdade é que nunca encontramos
aquela que atende às nossas necessidades completamente.
O Genesis, que é uma opção a mais, é uma ferramenta de desenvolvimento que busca a
união entre os esforços de documentação, criação do Banco de Dados e programação,
auxiliando na construção do software, a partir dos dados inseridos pelo usuário uma
única vez, nestes três aspectos básicos:
Geração do Banco de Dados, com todas as tabelas e seus respectivos relacionamentos;
Geração do código das classes, uma para cada tabela, contendo os atributos e os
métodos básicos, além da classe de conexão com o BD e o módulo de importação de
classes;
Geração dos relatórios de estrutura das tabelas, dicionário de dados e scripts de
criação das tabelas, podendo servir para análise do esquema ou fazer parte da
documentação do sistema.
104
Para alcançarmos nosso objetivo de programar orientado a objetos, agora utilizando
ferramentas que aumentem nossa produtividade, nesta etapa vamos conhecer um pouco
mais do Genesis, e descobrir como ele poderá nos ajudar em nossas atividades.
Utilizando o Genesis
A ferramenta foi desenvolvida no padrão do Access 2000, para que possa ser utilizado
pelo maior número de usuários. Além das bibliotecas padrão do Access, ele utiliza as
seguintes referências, na ordem em que aparecem:
OLE Automation
Microsoft ActiveX Data Objects 2.1 Library
Microsoft DAO 3.6 Object Library
Caso sua versão possua referências diferentes, desmarque as existentes, que deverão
estar indicadas como " AUSENTES ", e procure uma equivalente, a mais próxima
possível. Normalmente elas têm o mesmo nome, alterando apenas o número da versão.
Quando encontrá-la marque, compile e teste o programa até que encontre todas as
bibliotecas necessárias.
Visando padronizar o estilo das classes e do banco de dados que serão criados, siga os
padrões propostos para nomear tabelas e atributos, além de inserir descrições conforme
será apresentado nos próximos tópicos. Caso você utilize outro padrão a funcionalidade
não será afetada, mas os atributos, as tabelas e as classes geradas pelo Genesis tem seus
105
nomes modificados ou são acrescentadas iniciais que podem prejudicar a estética do
sistema caso as regras não seja seguidas.
Como todo bom software livre o código do Genesis é aberto, e você pode realizar
modificações a seu critério para personalizar a padronização dos nomes de tabelas,
atributos e classes.
Campos e Comandos do Sistema
A figura abaixo identifica cada um dos botões de comando e campos da tela principal
do Genesis. Utilize-a como referência para os tópicos seguintes, que esclarecerão com
mais detalhes o funcionamento do sistema.
Trabalhando com Tabelas
Padrão proposto para nomes de tabelas:
:: Primeira letra em maiúscula;
:: Letras restantes em minúscula;
:: Nomes compostos por mais de uma palavra separados por maiúscula;
:: Não utilize acentos ou cedilha.
Ex: Departamento, Produto, Cliente, TipoCliente e CategoriaProduto
106
Quando o Genesis cria uma classe para a tabela é acrescentado o prefixo cls no início do
nome da mesma, resultando em uma classe com a inicial minúscula e as separações em
maiúscula. Os exemplos acima ficariam assim:
Ex: clsDepartamento, clsProduto, clsCliente, clsTipoCliente e clsCategoriaProduto
Caso você colocasse um nome, por exemplo, em minúscula, não haveria a separação das
palavras. Nomes com underscore também prejudicariam a estética, resultando em
nomes de classe da seguinte maneira:
Ex: clsdepartamento, clsPRODUTO, clsTipo_Cliente e clsCATEGORIA_PRODUTO
Para criar uma nova tabela clique no botão Novo (1). Será criado um registro em
branco.
Coloque o nome da tabela que deseja criar no campo Tabela/Classe (2), e a descrição
da mesma ao lado, no campo Descrição (3), para que esta seja adicionada aos relatórios
de documentação (estrutura da tabela e dicionário de dados) e ao relatório SQL.
Apenas as tabelas que estiverem com o campo Criar (4) marcado serão exportadas
quando o botão Exportar Classes (5) for clicado. Para excluir uma tabela clique no
botão Excluir (7). Se uma tabela for excluída todos os seus campos também serão
automaticamente eliminados.
Para navegar entre tabelas e pesquisar utilize os botões de navegação (5) e pesquisa
(6).
Depois de criada a tabela inclua os atributos.
Trabalhando com Campos da Tabela
Padrão proposto para os campos das tabelas:
:: Primeira letra em minúscula;
:: Letras restantes em minúscula;
:: Nomes compostos por mais de uma palavra separados por maiúscula;
:: Campos de chave primária ou estrangeira em que será utilizado um código inclua a
inicial cod ou id no nome do campo;
:: Não utilize acentos ou cedilha.
Ex: codDepartamento, descricao, valor, codCliente, idCliente, dataNascimento,
mesEntradaProduto e categoriaProduto
Quando o Genesis cria um atributo de classe para um campo de tabela é acrescentado
um prefixo que indica o tipo de dado, conforme a descrição a seguir:
107
String (texto) → str
Date/Time (data/hora) → dtm
Integer (inteiro) → int
Long (inteiro longo) → lng
Single (decimal simples) → sgl
Double (decimal duplo) → dbl
Currency (valor monetário) → cur
Object (objeto) → obj
Recordset (conjunto de registros) → rst
Boolean (lógico) → boo
Além disso a primeira letra do nome do atributo é convertida para maiúscula para
manter o padrão. Os exemplos acima ficariam assim:
Ex: intCodDepartamento, strDescricao, curValor, intCodCliente, lngIdCliente,
dtmDataNascimento, intMesEntradaProduto e strCategoriaProduto
Os próximos tópicos descrevem como configurar os campos da tabela a serem criados.
Chave Primária
Marque o campo PK (17) para indicar que o atributo é ou faz parte da chave primária da
tabela. Ao indicar um campo como chave primária ele aparecerá na caixa de
combinação do campo Atributo FK (22) de outras tabelas, para que possa ser usado
como chave estrangeira.
Obs: Atributos do tipo Memo ou Object não podem ser chave primária.
Nome do Atributo
Informe no campo Atributo (18) neste campo o nome do atributo, de acordo com o
padrão apresentado para nomes de campos de tabela.
Campo Obrigatório
Marque este campo para indicar que o atributo é obrigatório e não pode conter um valor
nulo.
Tipo de Dado
Escolha o tipo de dado para o atributo. Os tipos possíveis são:
String → Texto com até 255 caracteres;
Memo → Texto com tamanho indeterminado;
Date/Time → Data e/ou hora, tamanho 8 bytes;
Integer → Número inteiro com 2 bytes de tamanho (-32.768 a 32.767);
Long → Número inteiro longo com 4 bytes
de tamanho (-2.147.483.648 a 2.147.483.647);
Single → Decimal com ponto flutuante com 4 bytes
de tamanho (±3,402823E38 a ±1,401298E-45);
Double → Decimal com ponto flutuante com 8 bytes
108
de tamanho (±1,79769313486232E308 a ±4,94065645841247E-324);
Currency → Número com ponto fixo com 15 dígitos à esquerda da vírgula
e
4 dígitos à direita da vírgula, utilizado para cálculos de precisão
por
envolver valores em
dinheiro (-922.337.203.685.477,5808 até 922.337.203.685.477,5807);
Object → Endereços com tamanho de 4 bytes, que se referem a
objetos (Imagens, documentos, planilhas, etc...);
Boolean → Número com tamanho de 2 bytes que representa os valores
lógicos True
e False. A equivalência entre os valores e os números é 0 para False e
qualquer
outro número para True.Um valor True convertido para número retorna 1.
Tamanho do Campo
Informe no campo Tamanho (21) a quantidade máxima de caracteres para campos do
tipo String. O tamanho mínimo permitido é 1 e o máximo é 255 caracteres. Caso seja
colocado um valor fora do intervalo o Genesis informará o erro e colocará o valor
padrão. Os outros tipos de campo não podem ter tamanho de campo diferente do padrão.
Estes são os tamanhos padrão para cada tipo de campo:
String → 30;
Memo → 0 (Não há limite definido!);
Currency, Double e Date/Time → 8;
Integer e Boolean → 2;
Object, Long e Single → 4.
Chave Estrangeira (Foreign Key)
Indique no campo Atributo FK (22) o atributo (chave primária da tabela de destino) que
servirá de referência para a chave estrangeira na tabela à qual estará vinculado.
Os possíveis atributos são listados e podem ser acessados inclusive a partir da mesma
tabela. Isto ocorre quando, por exemplo, um atributo referencia outro atributo da mesma
tabela, criando um auto-relacionamento. Esta situação poderia ocorrer em casos como o
de uma empresa onde um funcionário é chefe de outro, ou um departamento é
subordinado a outro, ou quando um produto é componente de outro, e a melhor forma
de mapear este relacionamento seja uma tabela única.
Você só pode criar uma chave estrangeira que referencie um campo de chave primária
de outra tabela, ou da mesma tabela, se os dois campos forem do mesmo tipo. Como
exemplo um campo do tipo Integer não pode referenciar um String, e assim
sucessivamente.
Ao vincular campos de chave estrangeira o Genesis criará os relacionamentos com
integridade referencial, porém não incluirá os eventos em cascata para atualização e
exclusão de dados. Caso seja necessária, esta configuração deverá ser feita
manualmente pelo desenvolvedor no banco de dados criado pelo Genesis.
109
Tipo de Chave Estrangeira
Ao incluir uma chave estrangeira na tabela com certeza você fará uso desta ligação
também nas classes. Logo devemos informar o tipo de chave estrangeira no campo Tipo
FK (23). Para compreender a utilidade deste recurso, imagine os dois casos a seguir:
No primeiro temos uma relação entre Fita e Categoria, duas classes de uma locadora
de fitas de vídeo. Nesta locadora o valor de locação não é colocado individualmente nas
fitas. Elas são classificadas em categorias, como Lançamento, Catálogo, Infantil e
Promoção, para que seja facilitado o trabalho de alteração de preços. Ao se modificar o
valor de locação de uma categoria todas as fitas estarão atualizadas automaticamente.
1º Caso: Descrição de Tipo (DT)
Veja bem, no código da classe Fita deverá existir um método novaLocacao() ou algo
parecido, que será responsável pela efetivação de uma locação de fita. Para calcular o
valor da locação o método teria que instanciar a classe Categoria, verificar a que
categoria a fita pertence, através do atributo intCategoria, e solicitar a ela o valor da
locação.
Até aí tudo bem, mas então qual a utilidade do recurso Descrição de Tipo? Pois bem,
este recurso cria automaticamente um atributo do tipo de objeto da classe que precisa
ser instanciada (Categoria), e toda vez que o atributo de chave estrangeira intCategoria
é atualizado, a referência à classe Categoria também é atualizada através do atributo
objeto criado.
No exemplo dado os atributos da classe Categoria podem ser acessados através da
classe Fita, da seguinte maneira:
'Declaramos algumas variáveis conforme a necessidade
Dim curPrecoLocacao As Currency
Dim strDescricao As String
'Declaramos e instanciamos o objeto Fita
Dim objFita As New clsFita
'Obtemos o objeto fita cujo código é 1012
110
objFita.obter(1012)
'Acessamos os atributos da classe Categoria
strDescricao = objFita.objCategoria.descricao
curPrecoLocacao = objFita.objCategoria.valorLocacao
'De dentro da classe Fita o acesso seria mais simples, da seguinte
maneira
strDescricao = objCategoria.descricao
curPrecoLocacao = objCategoria.valorLocacao
Vamos analisar agora o segundo caso, o Vetor de Registro (VR). A descrição de tipo
serve para buscarmos um valor único em outra classe, mas imagine, como no exemplo
abaixo, um Departamento que queira obter uma relação com todos os Funcionários
que trabalham no mesmo. Uma solução seria criar um Recordset, executar uma
consulta e navegar através dele para coletarmos os dados desejados.
Visando facilitar este trabalho, ao se definir uma chave estrangeira como VR, o Genesis
inclui automaticamente um método vetorNomeDaClasse() na classe atual, cujo valor
de retorno é exatamente um Recordset contendo todos os objetos que pertencem ao
objeto que possui o método, neste caso todos os funcionários que pertencem ao
departamento.
2º Caso: Vetor de Registro (VR)
Veja no modelo de classes que, após definir intDepartamento como chave estrangeira
do tipo VR, foi criado o método vetorFuncionario() na classe Funcionario, o qual
utiliza o valor do atributo para saber a que Departamento pertence o funcionário. A
consulta é feita via SQL, diretamente no banco de dados, mas a atribuição dos valores
foi originada anteriormente pelos atributos do objeto.
Além disso foram incluídos dois atributos na classe estrangeira, aquela que necessitava
da relação de registros da outra classe. Foram criados um objeto da classe que contém o
VR e um Recordset que recebe os registros retornados da consulta. A operação é
executada toda vez que os atributos chave da classe estrangeira são atualizados. Neste
111
caso quando codDep é alterado pelo procedimento Property Let codDep(), os atributos
objFuncionario e rstFuncionario também são atualizados pelo procedimento.
A partir daí podemos navegar pelo Recordset rstFuncionario normalmente através da
classe Departamento, ou acessar suas propriedades, da seguinte maneira:
'Navegando pelo Recordset, buscando o próximo registro
objDepartamento.rstFuncionario.moveNext
'Verificando a quantidade de registros de funcionários
objDepartamento.rstFuncionario.recordCount
'Acessando um atributo do funcionário
objDepartamento.rstFuncionario.Fields("nome")
Obs: Perceba que ao acessarmos um atributo pelo recordset devemos
utilizar o
nome original do mesmo, pois o prefixo str adicionado existe somente
dentro
da classe.
Devemos nos lembrar que em nenhum momento o programador necessita instanciar as
classes ou os Recordsets para implementar estes recursos, pois o Genesis deixa todo o
código pronto. Ao programador cabe apenas instanciar a classe com a qual vai trabalhar
e fazer uso das facilidades apresentadas.
Importante:
* O recurso Descrição de Tipo só é implementado automaticamente quando a classe
estrangeira possui chave primária única. Quando a classe estrangeira possui chave
primária composta por mais de um atributo o Tipo FK será ignorado.
* O recurso Vetor de Registro só é implementado automaticamente quando as duas
classes possuem chave primária única. Nas classes em que a chave primária é composta
de mais de um atributo o Tipo FK será ignorado.
Ordem da Chave Estrangeira
Para definir a ordem das chaves estrangeiras no banco de dados a ser criado devemos
informá-la no campo Ordem (24).
Este recurso tem a única, mas não menos importante, utilidade de separar chaves
estrangeiras para uma mesma chave primária da tabela estrangeira. Isto ocorre quando
temos dois ou mais atributos semelhantes em uma tabela fazendo referência ao mesmo
atributo FK de outra tabela. Raramente isto acontece, mas no caso de ocorrer lembre-se
de utilizar a Ordem, ou ocorrerá um erro tanto na criação da tabela quanto da classe.
Para ilustrar melhor a necessidade veja um exemplo prático. Em um hospital os médicos
trabalham por escala de plantão. Digamos que o sistema do hospital mantém o registro
das equipes que concorrem ao plantão diariamente. Suponha que o BD esteja
organizado mais ou menos assim:
112
Podemos notar claramente que o sistema guarda os registros do médico que estava
escalado para o plantão e o médico que efetivamente tirou o plantão. Quando os dois
códigos são iguais significa que não houve substituição. Mas o importante é perceber
que tanto o atributo MedicoEscalado quanto MedicoSubstituto da tabela Plantao
referenciam a mesma chave primária da tabela Medico, ou seja, o atributo Codigo.
Para que o Genesis não confunda um caso como este com um caso de chave composta
por mais de um atributo, você deve diferenciar os atributos FK definindo Ordens
diferentes para os mesmos.
Poderíamos definir o campo Ordem para o atributo MedicoEscalado com o valor 1 e
para MedicoSubstituto com o valor 2, por exemplo. Nos casos de chave composta deve
ser mantida a mesma Ordem para todos os atributos, para o caso de apenas um conjunto
de chave estrangeira, ou uma ordem para cada conjunto, no caso de haver mais de um.
Em resumo, a Ordem representa uma seqüência, ou conjunto, de chaves estrangeiras
para uma mesma chave ou conjunto de chaves primárias.
Descrição do Atributo
Informe no campo Descrição (25) os detalhes que esclareçam a função do atributo, para
que ela seja adicionada aos relatórios de documentação (estrutura da tabela e dicionário
de dados) e aos comentários da classe. A descrição pode ter até 150 caracteres.
Lembre-se de utilizar texto que deixe claro a utilidade do atributo, e que esteja livre de
erros de ortografia ou gramática, zelando, assim, pela estética da documentação e do
sistema que será gerado.
Para campos de chave primária, a sugestão é colocar a sigla “PK – “ (Primary Key) no
início da descrição, para identificá-los como tal. Já para campos de chave estrangeira, a
sugestão é colocar a sigla “FK – “ (Foreign Key).
Excluindo um Atributo
Para excluir um atributo basta clicar no botão que contem uma lixeira (26) e confirmar
quando o sistema solicitar.
Gerando uma Classe
113
Para gerar o código da classe atual clique no botão Gerar Classe (13). O código será
criado e apresentado em um formulário independente. O campo Criar (4) não precisa
estar marcado para a utilização deste comando.
Um conhecimento prévio dos conceitos de Orientação a Objetos auxilia na compreensão
dos códigos desta parte. Em caso de dúvida consulte o restante do material ou uma das
referências apresentadas anteriormente para se aprofundar no assunto.
Primeiramente vamos conhecer a estrutura completa de uma classe gerada pela
ferramenta Genesis.
Nome da Classe → Conforme descrito anteriormente, o nome da classe é criado
adicionando-se o prefixo cls ao nome da tabela.
clsNomeDaTabela
Atributos → É criado um atributo para cada campo da tabela. Para cada campo de
chave primária é criado também um atributo de backup, utilizado pelo método salvar(),
pois no caso de ocorrer uma alteração de um atributo chave não seria mais possível
atualizar o BD pelo novo valor. Neste caso o método salvar() não faria efeito, então ele
utiliza o valor do backup para executar a consulta atualização, em seguida altera todos
os atributos para os novos valores, caso a operação ocorra com sucesso. Além destes
atributos particulares de cada classe, são criados dois atributos genéricos,
114
booChaveAlterada e booAtributoAlterado, ambos lógicos, utilizados para indicar
alterações nos valores de atributos chaves e atributos comuns, respectivamente. Estes
atributos não possuem função específica, e podem ser utilizados pelo desenvolvedor
conforme a necessidade, quando este quiser saber se uma chave ou atributo tiveram seus
valores modificados. Os atributos do tipo objeto e recordset automáticos são criados
caso exista algum atributo do tipo Descrição de Tipo (DT) ou Vetor de Registro (VR).
booChaveAlterada
booAtributoAlterado
bkpAtributoIdentificador1
tipoAtributoIdentificador1
: :
[bkpAtributoIdentificadorN]
[tipoAtributoIdentificadorN]
tipoAtributoExtra1
[tipoAtributoExtraN]
Métodos de acesso aos Atributos → Para cada atributo originado de um campo da
tabela são criados dois métodos, um para atribuir valor (Property Let, ou Property Set
para objetos) e outro para recuperar valor (Property Get). Dentro destes métodos podem
existir funções extras, como as que implementam o VR, dependendo do contexto.
Property Get chaveAlterada ( )
Property Get atributoAlterado( )
Property Let atributoIdentificador1( )
Property Get atributoIdentificador1( )
: :
Property Let atributoIdentificadorN( )
Property Get atributoIdentificadorN( )
Property Let atributo1( )
[Property Set atributo1( )]
Property Get atributo1( )
: :
Property Let atributoN( )
Property Get atributoN( )
[Property Get objNomeDoObjeto1( )]
: :
[Property Get objNomeDoObjetoN( )]
Métodos construtor e destrutor → Os métodos construtor e destrutor são criados sem
qualquer código executável. Apenas a estrutura deles é gerada. Para utilizá-los basta
inserir código dentro deles. Todo código que estiver dentro do método class_initialize()
será executado sempre que um objeto da classe for instanciado. Da mesma forma todo
código dentro de um método class_terminate() será executado sempre que a classe
encerrar a sua existência, mas antes que isto realmente ocorra.
class_initialize( )
class_terminate( )
115
Métodos de acesso ao Banco de Dados → São os métodos básicos de acesso ao banco
de dados, utilizados para realizar a persistência dos objetos. O método obter() é
utilizado para a recuperação de um objeto, recebendo como parâmetro o(s) valor(es)
do(s) atributo(s) chave da classe, e realizando uma consulta a partir deste(s) valor(es). O
método incluir() faz o oposto. Depois de definidos os valores dos atributos, este método
os coleta e inclui o novo objeto no banco de dados, tornando-o persistente. O método
salvar() grava as alterações feitas aos atributos do objeto no banco de dados. Um objeto
só pode ser salvo se já existia anteriormente. Objetos novos devem ser incluídos antes
que se possa salvá-los. Antes de salvar um objeto o método verifica se o mesmo já não
existe no banco de dados, utilizando o método existe(), que apenas checa a existência de
um registro com o mesmo valor de chave na respectiva tabela. O método excluir()
apaga o registro referente ao objeto no banco de dados. Todos este métodos retornam
verdadeiro caso a operação ocorra com sucesso, ou falso em caso contrário.
existe( )
incluir( )
excluir( )
obter( )
salvar( )
Limpar → Método que redefine todos os atributos de volta aos seus valores iniciais, ou
seja, atribui o valor Nulo para todos os atributos e False para os atributos
booChaveAlterada e booAtributoAlterado. O método é executado toda vez que um
objeto é excluído com sucesso, imediatamente após o término da operação. Você pode
utilizá-lo a qualquer momento para "limpar um objeto".
limpar()
Vetor de Registro (VR) e Descrição de Tipo (DT) → Os atributos e o método criados
para a implementação da descrição de tipo e do vetor de registro foram detalhados no
item que trata sobre o campo TipoFK. São os objetos que referenciam outra classe para
obter valores de atributos da mesma, no caso da DT, ou uma relação de registros, no
caso do VR. Estes atributos e o método são gerados apenas quando o usuário define
uma chave estrangeira com um dos dois tipos.
[objNomeDaClasse]
[rstNomeDaClasse]
[objetoNomeDoObjeto]
[vetorNomeDaClasse( )]
Gerando o Código SQL da Tabela
Para gerar o script SQL de criação da tabela clique no botão Gerar SQL (14). O código
será criado e uma mensagem de confirmação informará o sucesso da operação.
Visualizando o Código SQL da tabela e o Código da Classe
Para visualizar o script SQL de criação da tabela clique no botão Código SQL (16). O
código será apresentado em um formulário independente. O código pode ser copiado ou
alterado, mas esta alteração não fará efeito na criação do Banco de Dados.
116
Para visualizar o código da classe clique no botão Código da Classe (15). O código
será apresentado em um formulário independente. O código pode ser copiado ou
alterado, mas sua alteração não fará efeito na exportação das classes.
Visualizando o Relatório de Estrutura das Tabelas
Para visualizar o relatório de estrutura das tabelas clique no botão Listar Tabelas (10).
O relatório com as informações solicitadas será apresentado.
Visualizando o Dicionário de Dados
Para visualizar o dicionário de dados clique no botão Dicionário de Dados (11). O
relatório com as informações solicitadas será apresentado.
Visualizando o Relatório SQL
Para visualizar o relatório de código SQL das tabelas clique no botão Código SQL (12).
O relatório com as informações solicitadas será apresentado. Para exportá-lo para o
Word utilize a função Publicar com o MS Word
Gerando o Banco de Dados
Para gerar o banco de dados completo clique no botão Banco de Dados (8). A caixa de
diálogo do Windows Salvar Como será apresentada. Escolha a pasta e o nome do novo
banco de dados e clique em Salvar. Caso não ocorram erros uma mensagem será
apresentada ao término do processamento informando que o BD foi gerado com
sucesso.
Para colocar o projeto em andamento, exporte as classes para uma pasta e, em seguida,
importe-as para o BD que acaba de ser criado. Para isso abra o editor do Visual Basic e
vá ao menu Arquivo / Importar Arquivo e inclua as classes. Esta operação só permite
a importação de uma classe por vez, então seja paciente. Para ajudar com esta
importação, ou mesmo a atualização de classes, o Genesis inclui o módulo IRModulos,
que contem as funções importaClasses() e removeClasses(), que serão detalhadas em
um tópico a parte.
117
Na ocorrência de erros cada um deles será informado, sendo que isto não interrompe o
processo de criação do BD. As mensagens tentarão informá-lo sobre o erro para que
seja corrigido. Ao final será apresentada a mensagem de conclusão, na qual constará o
número de erros ocorridos.
A mensagem final irá sugerir a você que verifique e organize a estrutura estética dos
relacionamentos, pois eles são criados em ordem aleatória, fazendo com que as tabelas
fiquem em posições que deixam as linhas dos relacionamentos cruzando-se entre si, de
maneira totalmente caótica. Nada que interfira no funcionamento, mas para que você
mantenha seu modelo adequadamente limpo, um pouco de ordem não fará mal.
A Classe de Conexão
A classe aclConexaoBD será incluída no banco de dados criado, possibilitando a
utilização imediata a partir das outras classes ou qualquer outra função que necessite
acesso às tabelas via SQL. As classes geradas pelo Genesis já utilizam a classe de
conexão.
Importando e Removendo Módulos
A fim de evitar que os usuários tenham que importar ou remover módulos
individualmente do sistema, utilizando o menu do editor do VBA, ou a combinação de
teclas Ctrl+M, foi incluído no Genesis o módulo IRModulos, que possui duas funções,
uma para importar e outra para remover módulos em grupo.
Os procedimentos devem ser executados manualmente pelo desenvolvedor, que deverá
ajustar as variáveis que indicam o caminho da pasta onde se encontram as classes,
posicionar o cursor dentro do procedimento desejado e teclar F5 ou abrir o menu
Executar => Executar Sub/UserForm no menu do editor do VBA.
Este é o código dos procedimentos do módulo:
Sub importaClasses()
'Como utilizar este procedimento para
'importar seus módulos: primeiro crie as
'classes utilizando o Genesis, exporte-as
'para uma pasta qualquer, altere o endereço
'da variável caminho abaixo, e execute o
'procedimento, posicionando o cursor dentro
'do código e pressionando a tecla F5.
Dim fs, pasta, arquivos, arquivo
Dim caminho As String
Dim nomeCompleto As String
118
Dim nomeArquivo As String
'Altere o caminho da pasta para o
'local onde estão as suas classes
'a serem importadas.
caminho = "C:\MeuProjeto\Classes"
If caminho <> "" Then
Set fs = CreateObject("Scripting.FileSystemObject")
Set pasta = fs.GetFolder(caminho)
Set arquivos = pasta.Files
For Each arquivo In arquivos
nomeArquivo = arquivo.Name
If Left(nomeArquivo, 3) = "cls" Then
nomeCompleto = arquivo.Path
Application.VBE.ActiveVBProject.VBComponents.Import
nomeCompleto
End If
Next
End If
End Sub
Sub removeClasses()
'Para remover os módulos execute o
'procedimento posicionando o cursor
'dentro do código e pressionando F5.
Dim componentes, componente
Dim nomeComponente As String
Set componentes = Application.VBE.ActiveVBProject.VBComponents
For Each componente In componentes
nomeComponente = componente.Name
If Left(nomeComponente, 3) = "cls" Then
Application.VBE.ActiveVBProject.VBComponents.Remove
componente
End If
Next
End Sub
Caso seja necessário realizar alguma alteração em qualquer dos atributos ou das tabelas,
incluir ou retirar campos, ou mesmo corrigir descrições, remova as classes do projeto,
faça as alterações no Genesis, exporte novamente as classes e importe-as novamente.
Tudo isto pode ser feito em questão de minutos.
Tenha o cuidado de deletar as classes exportadas anteriormente, pois quando elas são
sobrepostas e a nova classe for menor que a anterior, o final da primeira não será
apagado, resultando em uma classe com erros no código.
Obs: Perceba que os procedimentos testam se o nome do componente é iniciado pelo
prefixo cls. Logo somente módulos que se iniciam com o prefixo serão importados ou
removidos. Para importar ou remover outros módulos basta ajustar o prefixo conforme a
119
necessidade. Este modelo apenas foi ajustado para trabalhar com as classes criadas pelo
Genesis, cujos nomes se iniciam por cls.
Exportando as classes
Para gerar e exportar todas as classes de uma só vez clique no botão Exportar Classes
(9). A caixa de diálogo do Windows Procurar Pasta será apresentada. Escolha a pasta
para onde deseja exportar suas classes e clique em OK. Caso não ocorram erros uma
mensagem será apresentada ao término do processamento informando que as classes
foram exportadas com sucesso e exibindo o caminho completo da pasta escolhida.
Atenção: Somente as tabelas que estiverem com o campo Criar (4) marcado serão
geradas e exportadas.
Encerrando o Sistema
Para encerrar o Genesis basta clicar no botão de encerramento (27), localizado no
canto superior direito da tela principal.
Download do Genesis
Será disponibilizado o link para download do Genesis, assim você poderá sempre obter
a última versão do sistema, contendo as atualizações mais recentes.
Genesis.zip
Utilizando este sistema com toda certeza seu trabalho de desenvolver projetos
orientados a objetos no Access/VBA será reduzido consideravelmente. Planeje seu
modelo de banco de dados, transcreva para o programa, exporte suas classes, gere seu
banco de dados e importe as classes. Pronto, seu novo projeto está iniciado.
Conclusão
Depois de aprender a trabalhar com classes e objetos, codificando manualmente todos
os atributos e métodos chega a hora de conhecer uma forma de automatizar esta tarefa.
A ferramenta Genesis nos possibilitou esta grande ajuda, poupando-nos tempo e
esforço.
Vimos como criar tabelas e incluir campos, ajustando seu tamanho, tipo e referências.
Vimos também como incluir informações para a documentação do sistema. Finalmente
pudemos verificar como agilizar a inclusão ou remoção de módulos de nosso projeto
criado. Todas estas facilidades complementam o conhecimento adquirido durante o
aprendizado decorrente dos meses de estudo.
Este artigo foi uma pequena adaptação da ajuda contida no programa, resultado de uma
revisão geral e a conseqüente atualização da versão anterior do Genesis, incluindo o
próprio arquivo de ajuda.
E agora, que caminho seguir? Como continuar a jornada do aprendizado?
120
Calma, no próximo artigo, o último da série, faremos uma abordagem de como utilizar
os objetos presentes no MS-Office para aprimorar seus projetos e direcionar suas novas
pesquisas. Novas fronteiras serão visualizadas, prontas para serem exploradas.
Até lá então...
121
Classes X - Conclusão
Após a longa jornada, na qual aprendemos a criar nossos próprios objetos, aplicando-os
em nossos projetos baseados em Visual Basic for Applications, chega o momento da
despedida. Com isso chega também a hora em que o desenvolvedor iniciará sua
caminhada solitária rumo ao aperfeiçoamento de suas habilidades.
Como prosseguir? O que devo fazer agora? Como aprofundar e aprimorar o que foi
aprendido? Onde buscar novos conhecimentos? Como me atualizar constantemente?
Este último artigo da série tentará responder algumas destas perguntas, com o intuito de
abrir a sua mente e fornecer caminhos para que você possa seguir em frente.
Utilizando os Objetos do Sistema
Praticamente todos os itens em um software desenvolvido em Access são objetos.
Sendo assim estes objetos podem ser acessados e trabalhados. Conhecendo seus
métodos, propriedades e eventos fica bem mais fácil para o desenvolvedor aproveitar
funcionalidades já existentes para construir seus aplicativos.
Entre os itens mais utilizados em um sistema Access/VBA estão o formulário e o
relatório, com seus controles, o recordset e o objeto DoCmd. Além destes existem
dezenas, ou centenas, de outros objetos que podem ser utilizados, mas estes são os
exemplos mais comuns e mais simples disponíveis.
Você pode realizar qualquer tarefa com um formulário ou relatório, bem como dos
controles existentes no mesmo, através de linhas de código. Para isto basta acessar o
objeto. Estando em um módulo de um formulário ou relatório você pode acessar seus
métodos e propriedades utilizando a palavra chave Me. Como exemplo, depois de
alterarmos algum dado na fonte de origem de um formulário e desejarmos que as
alterações sejam exibidas imediatamente para o usuário, basta realizarmos uma
chamada ao método Requery do mesmo para que as informações sejam atualizadas.
Neste caso fazemos a chamada diretamente, sem a necessidade de instanciar o objeto,
assim:
'Atualizando o conjunto
'de registros do formulário
Me.Requery
Caso seja necessário acessar um objeto formulário a partir de outro módulo devemos
primeiro instanciá-lo, pois seus métodos e propriedades não estarão acessíveis antes
disso.
'Definindo a variável de objeto
'para o formulário.
Dim frmCliente As Form
'Definindo a constante de twips
'para cálculo de centímetros
Const intTwips = 567
122
'Atribuindo o formulário FCliente
'à variável de objeto formulário
Set frmCliente = Form_FCliente
'Exibindo a legenda do formulário
'através da propriedade Caption
MsgBox frmCliente.Caption
'Alterando a propriedade Permitir
'Edição do formulário
frmCliente.AllowEdits = False
'Movendo o formulário para a posição de
'esquerda a 10cm e superior a 5cm
frmCliente.Move 10 * intTwips, 5 * intTwips
Para trabalhar com acesso aos dados armazenados nas tabelas podemos utilizar um
objeto do tipo recordset. Veja o exemplo que permitiria buscar os registros da tabela
Cliente e exibir o nome de cada cliente.
'Definindo a variável para
'consulta SQL
Dim strSql As String
'Definindo uma nova variável de objeto
'recordset para conjunto de registros
Dim rstCliente As New ADODB.Recordset
'Montando a consulta SQL
strSql = "Select * From Cliente"
'Abrindo o recordset
rstCliente.Open strSql, CurrentProject.Connection, adOpenStatic,
adLockReadOnly
'Exibindo o total de clientes
MsgBox "Total de clientes: " & rstCliente.RecordCount
'Exibindo o nome de cada um dos
'clientes existentes no recordset
While Not rst.EOF
MsgBox "Nome do Cliente " & rstCliente.AbsolutePosition & ": " & _
rstCliente("nomeCliente")
rstCliente.MoveNext
Wend
'Fechando o recordset
rstCliente.Close
Agora vamos supor que você tem um botão em cada um de seus formulários cuja
função é habilitar a edição dos dados, pois criou todos os formulários bloqueados para
edição. Enquanto a edição estiver liberada o formulário deverá mudar de cor para alertar
o usuário que ele está em modo de edição. O código do seu botão seria parecido com
este:
Sub btnLiberaEdicao_Click()
'Definindo a cor da seção detalhe para amarelo
Me.Section(acDetail).BackColor = vbYellow
123
'Definindo a propriedade permitir edição = true
Me.AllowEdits = True
'Atribuindo uma legenda informando que o formulário
'está liberado para edição
Me.Caption = "Atenção: Edição Liberada"
End Sub
Este código funcionaria corretamente liberando o formulário. O problema é que você
teria que colocá-lo em todos os botões de liberação de todos os formulários bloqueados.
Como nós já aprendemos o conceito de reutilização queremos aplicá-lo aqui para evitar
que qualquer alteração no código tenha que ser feita em mais de um lugar.
Então devemos criar um procedimento genérico que faça o trabalho independente do
formulário a ser liberado, em um módulo qualquer, onde todo o projeto tenha acesso:
Sub editaFormulario(argForm As Form)
'Definindo a cor da seção detalhe para amarelo
argForm.Section(acDetail).BackColor = vbYellow
'Definindo a propriedade permitir edição = true
argForm.AllowEdits = True
'Atribuindo uma legenda informando que o formulário
'está liberado para edição
argForm.Caption = "Atenção: Edição Liberada"
End Sub
Depois basta chamar o procedimento em qualquer formulário que necessite ser liberado
para edição, passando o formulário como parâmetro para o código:
Sub btnLiberaEdicao_Click()
'Chamando o procedimento que libera
'formulários e passando como argumento
'o próprio formulário
Call editaFormulario(Me)
End Sub
Claro que após a edição e o salvamento dos dados o formulário teria que ser bloqueado
novamente, mas essa parte deixo como exercício prático para você.
O importante é perceber que caso você queira mudar a cor que será usada para
representar a edição do formulário terá que alterar apenas no procedimento
editaFormulario(), já que todos os botões utilizam o mesmo procedimento.
Outro objeto bastante utilizado em qualquer projeto com VBA é o DoCmd. Este é o
objeto responsável por executar as ações do Access a partir de comandos no Visual
Basic for Applications. Ele não precisa ser instanciado, pois é um objeto ativo, já
iniciado com a aplicação. Para utilizá-lo basta chamar um de seus métodos, passando ou
não um parâmetro, dependendo da ação desejada:
'Abrindo o formulário FCliente
DoCmd.OpenForm "FCliente"
124
'Maximizando uma tela
DoCmd.Maximize
'Fechando uma tela
DoCmd.Close
Para aprender a trabalhar com os objetos fornecidos pelo VBA você pode recorrer à
documentação acessando-a a partir do próprio editor. Pesquise sobre cada objeto que
desejar verificando seus métodos, propriedades e eventos.
Outra maneira ainda mais prática de pesquisar por estas informações é utilizando um
recurso próprio para esta finalidade: o Pesquisador de Objetos. O pesquisador é uma
ferramenta que facilita bastante a pesquisa por métodos, atributos e eventos de objetos
do sistema. Um ponto importante é que ele não se limita a objetos do Access, mas
qualquer código que estiver registrado em sua biblioteca, incluindo os objetos de todos
os aplicativos do Office.
Como exemplo suponha que queremos saber quais são todos os membros do objeto
caixa de combinação (combobox). Ao abrir o pesquisador, basta digitar combobox na
caixa de pesquisa (1), escolher a classe do objeto combobox (2) que todas as suas
propriedades aparecem na lista de membros (3).
Quer saber mais sobre o pesquisador de objetos? Veja o tutorial do amigo Avelino
Sampaio:
http://www.usandoaccess.com.br/tutoriais/tuto2.asp?id=1#inicio
Interagindo com Aplicativos do Office
Além de trabalhar com os objetos internos do Access, o VBA nos permite criar novos
objetos ou mesmo acessar objetos existentes de qualquer aplicativo do Office.
125
Imagine que você queira montar uma planilha no Excel com os dados das vendas do
mês, existentes no cadastro do seu banco de dados. Ou talvez gerar cartas de proposta
em Word para seus clientes registrados nas tabelas do Access. Mas você quer que o
usuário consiga fazer isto automaticamente, simplesmente clicando em um botão. Então
você deve utilizar os objetos da classe do aplicativo desejado.
O elemento responsável por criar um objeto é a função CreateObject. Para acessar um
objeto existente utilize a função GetObject. Veja os exemplos:
'Inicia o Microsoft Excel e cria um novo objeto PlanilhadeTrabalho.
Set ExcelWorksheet = CreateObject("Excel.Sheet")
'Inicia o Microsoft Excel e abre um objeto PlanilhadeTrabalho
existente.
Set ExcelWorksheet = GetObject("C:\Planilhas\MinhaPlanilha.xls")
'Inicia o Microsoft Word.
Set WordBasic = CreateObject("Word.Basic")
Criando um Objeto Planilha do Excel
Para exemplificar a interação com um objeto do Excel vamos criar uma planilha, incluir
um texto na célula A1, salvar a planilha em uma pasta do computador e fechar o Excel
após a tarefa.
'Declara uma variável de objeto
Dim minhaPlanilha As Object
'Como a variável ainda não foi vinculada faremos
'a vinculação tardia.
Set minhaPlanilha = CreateObject("Excel.Sheet")
'Torna o Excel visível através do objeto Application.
'Observe que o objeto minhaPlanilha possui um
'atributo que também é objeto, o Application, que possui
'outras propriedades internas
minhaPlanilha.Application.Visible = True
'Coloca um texto na célula A1 da planilha
minhaPlanilha.Application.Cells(1, 1).Value = "Esta é a coluna A,
linha 1"
'Salva a planilha no diretório C:\MinhaPlanilha.xls
'Obs: Salve como .xlsx no Office 2007 ou superior
minhaPlanilha.SaveAs "C:\MinhaPlanilha.xls"
'Fecha o Excel com o método Quit no objeto Application
minhaPlanilha.Application.Quit
'Libera a variável de objeto
Set minhaPlanilha = Nothing
Após criar um objeto você poderá acessá-lo novamente utilizando agora a função
GetObject():
'Declara uma variável de objeto
Dim minhaPlanilha As Object
126
'Busca a planilha criada anteriormente
'Obs: Lembre-se que no Office 2007 ou superior o padrão é .xlsx
Set minhaPlanilha = GetObject("C:\MinhaPlanilha.xls")
'Torna o Excel visível através do objeto Application.
minhaPlanilha.Application.Visible = True
'Tornando a pasta de trabalho visível
minhaPlanilha.Parent.Windows(1).Visible = True
'Coloca um texto na célula A2 da planilha
minhaPlanilha.Application.Cells(2, 1).Value = "Esta é a coluna A,
linha 2"
'Salva a planilha.
minhaPlanilha.Save
'Fecha o Excel com o método Quit no objeto Application
minhaPlanilha.Application.Quit
'Libera a variável de objeto
Set minhaPlanilha = Nothing
Veja o resultado:
As regras anteriores são válidas para qualquer objeto aplicativo diferente daquele em
que se está trabalhando. Com isto você poderá usar sua imaginação para realizar, com
sucesso, a interação entre aplicativos, buscando, exportando e visualizando informações
da maneira que lhe for mais conveniente.
Para saber como programar para cada um dos aplicativos pesquise na documentação que
acompanha o sistema. Ali você encontrará todas as informações necessárias para
compreender e aplicar os conceitos em seus projetos.
Alterações no Modelo de Objetos do Access
A cada nova versão de produtos Office várias alterações são implantadas. Novos itens
são incluídos, outros são alterados e ainda vários são removidos.
127
Com relação à criação de classes, métodos e propriedades personalizadas não houve
alterações perceptíveis aos usuários em nenhuma das versões, ao logo de todos esses
anos. Houve sim várias modificações no modelo de objetos do sistema.
As tabelas a seguir foram retiradas da documentação do Access 2007, e dizem respeito
às modificações ocorridas comparando-se a versão do Access 2000 com o próprio
Access 2007. Escolhi estas tabelas porque foram as mais resumidas que encontrei. Caso
fosse apresentar todas as evoluções do sistema desde a primeira versão teríamos que
iniciar outra série de artigos. Como o nosso foco é a criação e utilização de objetos,
deixei de lado o restante e trouxe apenas uma amostra de como pesquisar sobre
modificações das versões.
Para encontrá-las você deve abrir a ajuda de qualquer uma das versões, exibir o sumário
e procurar pelo item O que há de novo. O tópico sempre informa as alterações
ocorridas desde a última versão. No caso do Access 2007 a própria Microsoft incluiu
um item com o histórico de modificações desde a versão 2000. Assim, para poupar
trabalho, utilizei o que já estava pronto. Isto se chama reutilização, lembra-se? ;)
Resolvi apresentar apenas as modificações nos objetos mais utilizados pela maioria dos
desenvolvedores, ou seja, apenas serão exibidas as alterações nos objetos mais
conhecidos. Você poderá ver a lista completa de modificações acessando a
documentação da ajuda do editor do VBA do Access 2007.
AccessObject
Propriedades Status
FullName
Alterado
Sintaxe anterior
READ-ONLY FullName [STRING]
Nova sintaxe
READ-WRITE FullName [STRING]
Type
Alterado
Sintaxe anterior
READ-ONLY Type [INT32]
Nova sintaxe
READ-ONLY Type [ACOBJECTTYPE]
AcCommand
128
Propriedades Status
acCmdDataAccessPageBrowse Oculto
acCmdDataAccessPageDesignView Oculto
acCmdDemote Oculto
acCmdImport Oculto
acCmdMicrosoftScriptEditor Oculto
acCmdNewObjectDataAccessPage Oculto
acCmdPromote Oculto
acCmdPublish Removido
acCmdSaveAsASP Oculto
acCmdSaveAsDataAccessPage Oculto
acCmdSaveAsIDC Oculto
acCmdSelectDataAccessPage Oculto
acCmdShowOnlyWebToolbar Oculto
acCmdStopLoadingPage Oculto
acCmdViewDataAccessPages Oculto
Application
Propriedades Status
CurrentObjectType
Alterado
Sintaxe anterior
READ-ONLY CurrentObjectType [INT32]
Nova sintaxe
READ-ONLY CurrentObjectType [ACOBJECTTYPE]
DataAccessPages Oculto
DefaultWebOptions Oculto
FileSearch Oculto
Métodos Status
CreateControl
Alterado
Sintaxe anteriorr
CONTROL CreateControl (STRING FormName, ACCONTROLTYPE
ControlType, OPTIONAL ACSECTION Section = 0, OPTIONAL
VARIANT Parent, OPTIONAL VARIANT ColumnName, OPTIONAL
VARIANT Left, OPTIONAL VARIANT Top, OPTIONAL VARIANT
Width, OPTIONAL VARIANT Height)
Nova sintaxe
129
_CONTROL CreateControl (STRING FormName, ACCONTROLTYPE
ControlType, OPTIONAL ACSECTION Section = 0, OPTIONAL
VARIANT Parent, OPTIONAL VARIANT ColumnName, OPTIONAL
VARIANT Left, OPTIONAL VARIANT Top, OPTIONAL VARIANT
Width, OPTIONAL VARIANT Height)
CreateDataAccessPage Oculto
CreateReportControl
Alterado
Sintaxe anterior
CONTROL CreateReportControl (STRING ReportName,
ACCONTROLTYPE ControlType, OPTIONAL ACSECTION Section = 0,
OPTIONAL VARIANT Parent, OPTIONAL VARIANT ColumnName,
OPTIONAL VARIANT Left, OPTIONAL VARIANT Top, OPTIONAL
VARIANT Width, OPTIONAL VARIANT Height)
Nova sintaxe
_CONTROL CreateReportControl (STRING ReportName,
ACCONTROLTYPE ControlType, OPTIONAL ACSECTION Section = 0,
OPTIONAL VARIANT Parent, OPTIONAL VARIANT ColumnName,
OPTIONAL VARIANT Left, OPTIONAL VARIANT Top, OPTIONAL
VARIANT Width, OPTIONAL VARIANT Height)
NewCurrentDatabase
Alterado
Sintaxe anterior
VOID NewCurrentDatabase (STRING filepath)
Nova sintaxe
VOID NewCurrentDatabase (STRING filepath, OPTIONAL
ACNEWDATABASEFORMAT FileFormat = 0, OPTIONAL VARIANT
Template, OPTIONAL STRING SiteAddress = "", OPTIONAL STRING
ListID = "")
OpenCurrentDatabase
Alterado
Sintaxe anterior
VOID OpenCurrentDatabase (STRING filepath, OPTIONAL BOOL
Exclusive = 0)
Nova sintaxe
VOID OpenCurrentDatabase (STRING filepath, OPTIONAL BOOL
Exclusive = 0, OPTIONAL STRING bstrPassword = "")
130
ComboBox
Propriedades Status
AllowedText Oculto
DoCmd
Propriedades Status
ApplyFilter
Alterado
Sintaxe anterior
VOID ApplyFilter (OPTIONAL VARIANT FilterName,
OPTIONAL VARIANT WhereCondition)
Nova sintaxe
VOID ApplyFilter (OPTIONAL VARIANT FilterName,
OPTIONAL VARIANT WhereCondition, OPTIONAL VARIANT
ControlName)
OpenReport
Alterado
Sintaxe anterior
VOID OpenReport (VARIANT ReportName, OPTIONAL
ACVIEW View = 0, OPTIONAL VARIANT FilterName,
OPTIONAL VARIANT WhereCondition)
Nova sintaxe
VOID OpenReport (VARIANT ReportName, OPTIONAL
ACVIEW View = 0, OPTIONAL VARIANT FilterName,
OPTIONAL VARIANT WhereCondition, OPTIONAL
ACWINDOWMODE WindowMode = 0, OPTIONAL VARIANT
OpenArgs)
OutputTo
Alterado
Sintaxe anterior
VOID OutputTo (ACOUTPUTOBJECTTYPE ObjectType,
OPTIONAL VARIANT ObjectName, OPTIONAL VARIANT
OutputFormat, OPTIONAL VARIANT OutputFile, OPTIONAL
VARIANT AutoStart, OPTIONAL VARIANT TemplateFile)
Nova sintaxe
VOID OutputTo (ACOUTPUTOBJECTTYPE ObjectType,
131
OPTIONAL VARIANT ObjectName, OPTIONAL VARIANT
OutputFormat, OPTIONAL VARIANT OutputFile, OPTIONAL
VARIANT AutoStart, OPTIONAL VARIANT TemplateFile,
OPTIONAL VARIANT Encoding, OPTIONAL
ACEXPORTQUALITY OutputQuality = 0) ;
SelectObject
Alterado
Sintaxe anterior
VOID SelectObject (ACOBJECTTYPE ObjectType, OPTIONAL
VARIANT ObjectName, OPTIONAL VARIANT
InDatabaseWindow)
Nova sintaxe
VOID SelectObject (ACOBJECTTYPE ObjectType, OPTIONAL
VARIANT ObjectName, OPTIONAL VARIANT
InNavigationPane)
TransferSpreadsheet
Alterado
Sintaxe anterior
VOID TransferSpreadsheet (OPTIONAL
ACDATATRANSFERTYPE TransferType = 0, OPTIONAL
ACSPREADSHEETTYPE SpreadsheetType = 8, OPTIONAL
VARIANT TableName, OPTIONAL VARIANT FileName,
OPTIONAL VARIANT HasFieldNames, OPTIONAL VARIANT
Range, OPTIONAL VARIANT UseOA)
Nova sintaxe
VOID TransferSpreadsheet (OPTIONAL
ACDATATRANSFERTYPE TransferType = 0, OPTIONAL
ACSPREADSHEETTYPE SpreadsheetType = 10, OPTIONAL
VARIANT TableName, OPTIONAL VARIANT FileName,
OPTIONAL VARIANT HasFieldNames, OPTIONAL VARIANT
Range, OPTIONAL VARIANT UseOA)
Form
Propriedades Status
AllowDesignChanges Oculto
Section
Alterado
Sintaxe anterior
READ-ONLY Section [SECTION] (VARIANT Index)
132
Nova sintaxe
READ-ONLY Section [_SECTION] (VARIANT Index)
WhatsThisButton Oculto
ListBox
Propriedades Status
TextAlign Oculto
Report
Propriedades Status
Section
Alterado
Sintaxe anterior
READ-ONLY Section [SECTION] (VARIANT Index)
Nova sintaxe
READ-ONLY Section [_SECTION] (VARIANT Index)
TextBox
Propriedades Status
AllowedText Oculto
FELineBreak Oculto
Access em 64 bits
Uma das mudanças mais importantes que aconteceu recentemente foi o suporte para
aplicativos em 64 bits. Isto denota a chegada irremediável do futuro do
desenvolvimento. Hoje estamos vivendo uma época de transição em que aplicativos de
32 e 64 bits convivem lado a lado, deixando confusos tanto usuários quanto
desenvolvedores.
Porém, em um futuro próximo, as aplicações de 32 bits estarão obsoletas, assim como
as aplicações de 16 bits ainda existentes hoje. Então porque esperar para aprender sobre
133
esta novidade? Veja o tutorial feito também pelo amigo Avelino Sampaio sobre as
alteraçães necessárias para programar VBA em 64 bits:
http://usandoaccess.com.br/tutoriais/tuto28.asp?id=1#inicio
Atualizando o Conhecimento
Para quem está interessado em se manter atualizado com as novas tecnologias,
conceitos e tudo mais que cerca o desenvolvimento com as ferramentas do Office, o
principal ponto de referência é o próprio site da Microsoft para desenvolvedores. O
MSDN é o local onde se encontra toda a documentação sobre os aplicativos, as API´s e
as dicas da comunidade. Visite o site e veja quanta informação está disponível:
http://msdn.microsoft.com/pt-br/default.aspx
No entanto, há vários outros locais para se buscar o conhecimento. Na maioria dos sites
sobre o assunto você encontrará artigos, tutoriais e exemplos, além dos fóruns em que
participantes mais experientes geralmente tiram as dúvidas dos iniciantes, além das
próprias dúvidas ao trocar experiências com outros membros. Estes são alguns dos
principais:
Em Português:
www.usandoaccess.com.br
www.expertaccess.com.br
www.ativoaccess.com.br
www.juliobattisti.com.br
www.apostilando.com
www.imasters.com.br/secao/access
comunidade.itlab.com.br
www.scriptbrasil.com.br
www.linhadecodigo.com.br/access.aspx
www.marcoratti.net
Em Inglês:
www.mvps.org/access
www.lebans.com
www.functionx.com/vbaccess/index.htm
134
www.blueclaw-db.com/accessvisualbasic
www.excel-vba.com/excel-vba-contents.htm
www.allenbrowne.com/tips.html
Além de participar de fóruns e pesquisar em sites, além da própria ajuda do Access, é
extremamente importante ler bons livros sobre o assunto. Procure saber mais sobre
orientação a objetos, sobre acesso a dados, modelagem de banco de dados e padrões de
projeto.
Seu aperfeiçoamento depende agora exclusivamente de você. Eu ficaria muito feliz em
saber que você decidiu se tornar um desenvolvedor especialista no assunto incentivado
pelo que aprendeu nestes artigos. E ficaria mais feliz ainda se soubesse que você está
disseminando este conhecimento para outros interessados.
Chegamos ao Fim
Enfim chegamos ao final da série. Foi uma longa jornada em que tive que pesquisar,
estudar e planejar meu tempo, buscando janelas de horários em que pudesse me dedicar
a escrever os artigos, enquanto outras tarefas aguardavam ansiosas para seram também
continuadas.
A todos que esperaram com entusiasmo o lançamento de cada um dos artigos peço
desculpas pela demora no intervalo entre cada publicação, pois como devem imaginar
tudo foi feito nas horas vagas, inclusive finais de semana e feriados.
A todos que acompanharam a série um muitíssimo obrigado. Tenho certeza de que as
lições foram capazes de proporcionar o conhecimento mínimo necessário para que
desenvolvam projetos orientados a objeto com qualidade.
Espero nos encontrarmos em breve em mais um trabalho tão quanto ou mais gratificante
que este.
Abraços e até a próxima!
Fonte: http://www.mabesi.com/busca-avancada.html?searchword=CLASSES&searchphrase=all