Utilizando Classe no Access - As Classes Auxiliares
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
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
'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)
'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
'Soma = Soma + (valor da posição dentro do CPF x (12 - posição))
wSomaDosProdutos = wSomaDosProdutos + (Val(Mid(argCpf, wI, 1)) * (12 - wI))
'Soma = Soma (2 x dígito verificador 1)
wSomaDosProdutos = wSomaDosProdutos + (2 * wDigitChk1)
'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
'CPF inválido
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
'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
'Formato de e-mail válido
validaEmail = True
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 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 = ""
'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))
'Monta o nome convertendo o restante através da recursão
nomeProprio = sNome & nomeProprio(LCase(Trim(Right(argNome, lTamanho - lEspaco))))
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 strNovoTexto As String
Dim intPosicao 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)
'Remonta o novo texto, sem acento
strNovoTexto = strNovoTexto & strLetra
'Devolve o resultado
desacentua = strNovoTexto
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.
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 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.
If Mid(argNome, i, 1) = " " And ultimoEspaco <> 0 Then
penultimoEspaco = i
If Mid(argNome, i, 1) = " " And penultimoEspaco = 0 Then
ultimoEspaco = i
'Caso i chegue a zero não podemos
'abreviar o nome
abreviaNome = argNome
'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
abreviaNome = primeiraParte & " " & Left(parteNome, 1) & ". " & ultimaParte
A Classe ConexaoBD
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
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"
'Senão troca por False
logicoSql = "False"
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 strInteiro As String
'Converte o valor em string
strValor = CStr(varValor)
'Busca a posição da vírgula
intPosicao = InStr(strValor, ",")
'Se há uma vírgula em alguma posição
'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
'Senão devolve o mesmo valor
pontoVirgula = strValor
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 & "#"
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
valorSql = "Null"
'Caso seja inteiro ou longo apenas
'converte em string
valorSql = CStr(argValor)
'Caso seja simples, duplo, decimal ou moeda
'substitui a vírgula por ponto
Case vbSingle, vbDouble, vbDecimal, vbCurrency
valorSql = pontoVirgula(argValor)
'Caso seja data chama a função dataSql()
valorSql = dataSql(argValor)
'Caso seja string acrescenta aspas simples
valorSql = "'" & argValor & "'"
'Caso seja lógico chama a função logicoSql()
valorSql = logicoSql(argValor)
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
'retornando o número de registros afetados caso a operação ocorra
'com sucesso ou 0 caso ocorra algum problema
db.Execute codigoSql
executa = db.RecordsAffected
Exit_executa:
Err_ executa:
executa = 0
Resume Exit_executa
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
If editavel Then
Set consulta = db.OpenRecordset(codigoSql, , dbInconsistent, dbOptimistic)
Set consulta = db.OpenRecordset(codigoSql, dbOpenSnapshot, dbReadOnly)
Exit_consulta:
Err_consulta:
MsgBox "Erro SQL: " & codigoSql
Resume Exit_consulta
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")
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.
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
'Testando um método da classe, convertendo
'uma data para o padrão SQL
MsgBox objCon.dataSql("12/06/10")
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:
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.
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...
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.
Em nossa próxima etapa poderemos enfim ver o sistema de vendas funcionando pela primeira vez, pois iremos implementar a classe Cliente, bem como a interface gráfica para cadastro e manuseio dos dados, tudo isto sem campos acoplados, apenas usando os objetos da classe.
Marcos_Novack 22/04/2011 18:54:44
Sem duvida muito bom os ensinamentos
Notei que, no testeUtilitario() foi trocado o inicio do nome da classe, de "cls" para "acl"
nos testes anteriores começava com "cls"; notei porque deu erro.
Alex Sandro Risso 04/03/2011 14:46:32
Agradeço deveras as explicações, tornado essa fonte ama ajuda grandiosa para os iniciantes como eu
Alex Sandro Risso 04/03/2011 14:46:11
Plinio Mabesi 09/10/2010 16:05:37
Olá Ader
Sobre o erro não encontrei nenhum bloco faltando o fechamento com End If. Utilizo o Access 2007 para montar todos os exemplos, salvando em formato anterior, e até o momento não tive nenhum problema deste tipo.
Se puder verificar novamente seu código para ver se o erro está em outro local e me avisar, agradeço. Caso não encontre o problema e queira me mandar o bd com o trecho que está dando erro estarei a disposição.
Ader Santos 06/10/2010 13:27:21
Grande Plínio, sucesso total! Só tem uma coisinha, na função nomeProprio, falta fechar um bloco com End If, senão dá "tipos incompatíveis" no Access 2007. Abração.
Gilson Gindre 24/06/2010 07:35:09
Plinio, fiquei sem palavras, não sei como agradecer seu tamanho esforço para com todos nós da area !
para se ter uma ideia eu já estou programando em Php MySql, mas a paixão pela facilidade do access fala mais alto, e agora com estes recuros todos, estou sem palavras para agradecer você e o Avelino, e parabens pelo site, que veio para ficar !
Renato Novelli 23/06/2010 10:46:46
Exceleeeeente artigo! Muito obrigado por compartilhar com todos essa valiosíssima informação.
Será de muita utilidade para o desenvolvimento de minhas aplicações.
Esse tipo de material é realmente muito útil e valoriza em muito o trabalho dos desenvolvedores de aplicações em Access.
Estou ansioso para ver o próximo artigo!
Plinio Mabesi - Anápolis-GO 20/06/2010 12:59:31
Obrigado a vc e a todos que nos prestigiam...
Se vc colocar esta função em um módulo padrão (que não seja de classe nem de formulário) vc poderá fazer exatamente desta maneira que fez, porém coloque no evento Após Atualizar.
Rodrigo Monteiro 20/06/2010 12:37:56
Caríssimo Plinio......
Meus parabéns por sua disposição em compratilhar conhecimento....
Fiquei muito interessado na função "nomeProprio"......
Mas não consegui aplicá-la numa caixa de texto..... tentei assim no evento "ao atualizar": Campo=nomeProprio(Campo)
Poderia me dizer como pode ser usada essa função?!
João Martins/Joinville/SC 19/06/2010 21:46:55
Parece-me que a parceria de Avelino e o Profº Plinio esta produzindo resultados positivos e quiçá seja duradoura; tenho aprendido muito. Visionários e vencedores se destacam assim: - compartilhando seus conhecimentos, pois de nada servem enclausurados nas suas cabeças.
Falou Access, falou www.usandoaccess.com.br
Américo 18/06/2010 16:42:57
Tem uma aplicação feita para ilustrar um livro (Desenvolvendo Aplicativos Com Visual Basic e Uml) que usa conceitos de programação orientada a objetos, com uma GUI web. Praticamente tudo que foi implementado nesses exemplos pode ser importado e adaptado para VBA. Os fontes estão em: http://www.jacksonreed.com/VBUML.HTM.
Obs.: Para visualizar o projeto completo é necessário o VB6 instalado.
Márcio Melo - Rio de Janeiro/RJ 17/06/2010 22:47:45
Material nota 10, muito show, parabéns por pensar nessas funçoes, quem trabalhar com OO vai precisar muito, isso mostrar um trabalho completo e maduro, cada vez o site vai se tornando forte referência no access avançado...