Ir para o conteúdo
  • Cursos
  • Sobre
  • Conteúdo
  • Eventos
  • Contato
Menu
  • Cursos
  • Sobre
  • Conteúdo
  • Eventos
  • Contato
pedir orçamento
BLOG

VBA – Artigo 013 – Classes no VBA

 

Classes no VBA

Este artigo é extenso devido a quantidade de detalhes. Recomendo ler
quando tiver bastante tempo para ler e praticar os códigos de exemplo.
Há muitos conceitos aqui e pode levar um tempo para absorver tudo. Leia
e releia quantas vezes for necessário e use os comentários do blog se
houver necessidade.
 
Classe é uma ferramenta poderosa em programação orientada a objetos
(POO). Embora há quem diga que o VBA não seja totalmente orientado a
objetos, a falta de alguns conceitos de POO não impede de realizar ótimos
trabalhos. A classe serve para projetar um objeto, conceito principal
da POO. É preciso enfatizar o fato que a classe é um
projeto do objeto, ou seja, a classe só é utilizada para criar (ou
instanciar) os objetos.
No VBA a classe é definida em um módulo de classe, que é adicionado indo em
Inserir/Módulo de classe. É nesse módulo que você criará as
propriedades e os métodos. Ao criar um módulo de classe, a primeira coisa a
fazer é alterar a propriedade (Name) para um nome mais apropriado do
que Classe1, como por exemplo clsProduto. É boa prática
colocar o prefixo cls para diferenciar a classe dos objetos que serão
criados a partir dela. A criação do objeto é feita da seguinte maneira:
Dim Produto As clsProduto
Set Produto = new clsProduto
A primeira linha efetua a declaração (obrigatório quando se utiliza
Option Explicit), enquanto a segunda efetua a criação do objeto.
Somente a partir da criação de um objeto que é possível utilizar as
propriedades e os métodos da classe que serviu de origem. Do jeito que está,
o objeto Produto não tem utilidade nenhuma, pois não tem nenhuma
propriedade ou método.
Para criar propriedades, é preciso declará-las no módulo de classe. Coloque
as seguintes linhas no módulo clsProduto:
Option Explicit
Private pCodigo As Long
Private pNome As String
Private pQuantidade As Long
Private pPreco As Currency
Private pAtivo As Boolean
Perceba que todas as propriedades foram declaradas como privadas, ou
seja, só serão acessadas internamente nesse módulo. Também foi adicionado um
prefixo p de propriedade. Você pode definir o prefixo que
quiser, desde que fique fácil de distinguir o que será usado só pela classe
e o que será acessado pelo objeto.
As propriedades ainda não estão prontas para uso, falta os meios para
conseguir acessá-las e torná-las úteis de fato. Precisamos declarar
procedimentos que permitam esse acesso. Para isso temos de criar
Property Get e Property Let para cada propriedade. Vejamos
como fica a propriedade Nome:
Public Property Get Nome() As String
    Nome = pNome
End Property
Public Property Let Nome(Valor As String)
    pNome = Valor
End Property
Get serve para obter a propriedade, enquanto Let serve
para armazenar um valor na propriedade. Com este código já é possível
acessar a propriedade Nome. Atente bem ao tipo do objeto nas
declarações, que precisam combinar como tipo da propriedade. Da mesma forma,
perceba que a declaração dos procedimentos é pública, para que
possamos acessar. Se fosse declarado como privados a propriedade
seria interna do objeto e só acessada pelos próprios procedimentos do módulo
de classe. Não estranhe, há casos em que é necessário fazer dessa forma,
como veremos mais adiante.
Crie um módulo novo (módulo comum, não de classe) para testar essa
propriedade Nome com o seguinte código:
Sub TesteClasse()
    Dim Produto As clsProduto
    Set Produto = New clsProduto
    Produto.Nome = “Teste”
    Debug.Print “Nome do produto: ” & Produto.Nome
End Sub
Depure o código (tecla F8) e acompanhe como a execução percorre os
procedimentos, entrando em Property Let para armazenar o valor e
depois entrando em Property Get para obter o conteúdo da
propriedade.
Como você pode ter notado, Property Get e Property Let são
procedimentos como outros, sendo possível incluir uma consistência dos dados
enviados antes de armazenar. Veja a propriedade Quantidade abaixo:
Public Property Get Quantidade() As Long
    Quantidade = pQuantidade
End Property
Public Property Let Quantidade(Valor As Long)
    If Valor >= 0 Then
        pQuantidade = Valor
    Else
        Err.Raise Number:=1001,
Description:=”A quantidade não pode ser negativa”
    End If
End Property
O procedimento Property Let inclui uma validação e só armazenará o
valor caso seja maior ou igual a zero. Isso impede que nosso objeto tenha
quantidade negativa. Caso venha um valor inválido, um erro é gerado com
Err.Raise. Caso não conheça ou não entenda este método, visite meu
décimo segundo artigo, que explica o objeto Err.
Acrescente o código logo abaixo do Debug.Print em TesteClasse
e depure, depois altere o valor para negativo e veja o que acontece:
    Produto.Quantidade = 50
    Debug.Print “Quantidade do produto: ” &
Produto.Quantidade
Ainda temos duas propriedades a serem criadas no exemplo. A propriedade
Preco é bem similar à Quantidade, também precisando de uma
validação para impedir números negativos. Neste caso também vamos impedir o
valor 0:
Public Property Get Preco() As Currency
    Preco = pPreco
End Property
Public Property Let Preco(Valor As Currency)
    If Valor > 0 Then
        pPreco = Valor
    Else
        Err.Raise Number:=1002,
Description:=”O preço deve ser um valor positivo”
    End If
End Property
A propriedade Codigo é similar à propriedade Nome, sem
necessidade de consistência. Porém, se quisermos controlar o código, não
devemos permitir que procedimentos externos modifiquem o valor. Desta forma,
Property Let deverá ser declarada como private, ou seja, só o
próprio módulo de classe poderá modificar esta propriedade:
Public Property Get Codigo() As Long
    Codigo = pCodigo
End Property
Private Property Let Codigo(Valor As Long)
    pCodigo = Valor
End Property
Se você tentar associar um valor a Produto.Codigo no procedimento
TesteClasse irá receber uma mensagem de erro, alertando que esse
componente não existe. Desta forma impedimos que o seja associado um valor
que possa quebrar a ordem desejada ou até repetir um valor existente. A
propriedade Codigo foi criada como somente leitura, ou seja,
só podemos ler o valor, não modificar. É possível criar propriedades
somente escrita, neste caso Property Get deverá ser
privado enquanto Property Let deverá ser público.
Mas como iremos gerar um código para o produto? Vamos pensar um pouco: em
que momento precisamos gerar um número novo para um produto? Somente quando
for criar um novo. Portanto, o procedimento de adicionar produto deverá
buscar o último valor utilizado, incrementar 1 e definir esse valor como o
código do novo produto. Isso será feito mais adiante.
A propriedade Ativo, por sua vez, servirá para indicar se o produto
foi “excluído” ou não. Usaremos essa propriedade como uma
exclusão lógica, ou seja, a linha não será removida da tabela, apenas
constará como um produto inativo. Imagine que tenha orçamentos existentes
mencionando um determinado produto e então o apagamos da tabela: perderemos
os vínculos de dados nos orçamentos e também toda a integridade de dados,
levando a erros imprevisíveis. Por isso, é preferível fazer uma exclusão
lógica (marcar como inativo) ao invés de física (apagar a linha do produto
definitivamente). Como essa propriedade não deve ser acessada diretamente,
apenas por procedimentos, teremos Property Get e
Property Let declarados como privados:
Private Property Get Ativo() As Boolean
    Ativo = pAtivo
End Property
Private Property Let Ativo(Valor As Boolean)
    pAtivo = Valor
End Property
Até aqui terminamos a criação das propriedades, agora passaremos aos
métodos. Mas antes, criaremos uma planilha para colocar os
produtos. Crie uma planilha Produtos
e renomeie o valor da propriedade (Name) para plProdutos. Se não souber como fazer isso veja meu terceiro artigo. Crie um cabeçalho para os dados: Código, Nome, Quantidade, Preço e Ativo. Em seguida, entre no Gerenciador de nomes (tecla de atalho Ctrl + F3) e crie uma área nomeada dinâmica chamada tbProdutos
com a seguinte fórmula:
=DESLOC(Produtos!$A$1;0;0;CONT.VALORES(Produtos!$A:$A);5)
Note que nessa área nomeada foi incluído o cabeçalho. Isso garante que a
quantidade mínima de linhas seja 1, o que impede de acontecer erros por não
ter nenhuma linha com produto.

Há dois métodos de classe especiais: Class_Initialize
e Class_Terminate. Como os próprios nomes sugerem, eles se
referem à inicialização e à finalização da classe, respectivamente. Na
inicialização podemos definir valores iniciais a uma classe. Poderíamos
definir um valor inicial para as propriedades, mas não iremos usar esses
métodos neste artigo. Acrescente o procedimento a seguir no final do módulo da classe:

Public Sub Adicionar()
    Dim Produtos As Range
    Dim Linha As Long


    Set Produtos = plProdutos.Range(“tbProdutos”)
    Linha = Produtos.Rows.Count + 1
    If Produtos.Rows.Count = 1 Then ‘ Se só existe a linha
do cabeçalho
        Codigo = 1
    Else
        Codigo =
Produtos.Cells(Produtos.Rows.Count, 1).Value + 1
    End If
    Produtos.Cells(Linha, 1).Value = Codigo
    Produtos.Cells(Linha, 2).Value = Nome
    Produtos.Cells(Linha, 3).Value = Quantidade
    Produtos.Cells(Linha, 4).Value = Preco
    Produtos.Cells(Linha, 5).Value = True
End Sub
Por fim, no módulo de teste (onde há a sub-rotina TesteClasse)
acrescente o código a seguir:
Sub TesteAdicionar()
    Dim Produto As clsProduto
    Set Produto = New clsProduto
    Produto.Nome = “Teste”
    Produto.Quantidade = 50
    Produto.Preco = 10
    Produto.Adicionar
End Sub
Não precisamos acrescentar um valor para a propriedade Ativo porque
ao adicionar ele será sempre Verdadeiro (não faz muito sentido
incluir um produto inativo). Depure esse código, acompanhando cada passo do
processo. Veja como os valores das propriedades são adicionados e lidos.
Observe como grava uma linha na tabela. Repita o processo algumas vezes para
acrescentar algumas linhas, veja como incrementa o código. Remova algumas
linhas e acrescente mais para ver o resultado.
O próximo código será o último mais um, mas se o usuário alterar a ordem
das linhas pode gerar duplicidade de código. Por isso é preciso de um
procedimento que ordene a tabela de produtos antes de incluir um novo.
Podemos criar esse procedimento na própria classe como private, para
que somente a própria classe execute. Adicione esta rotina no módulo da
classe:
Private Sub OrdenarTabela()
    Dim Produtos As Range
    Set Produtos = plProdutos.Range(“tbProdutos”)
 
    plProdutos.Sort.SortFields.Clear
    plProdutos.Sort.SortFields.Add Key:=Produtos.Cells(1,
1), _
        SortOn:=xlSortOnValues,
Order:=xlAscending, DataOption:=xlSortNormal
   
    With plProdutos.Sort
        .SetRange Produtos
        .Header = xlYes
        .MatchCase = False
        .Orientation =
xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
End Sub
Em seguida, acrescente a chamada desse procedimento dentro de
Adicionar, logo após a declaração de variáveis, desta forma:
    Dim Produtos As Range
    Dim Linha As Long



    OrdenarTabela

   
    Set Produtos = plProdutos.Range(“tbProdutos”)
    Linha = Produtos.Rows.Count + 1
Altere a ordem de alguns produtos e experimente adicionar um novo. Você
verá que o método Adicionar passa a ordenar a tabela antes de
acrescentar um produto novo a lista, impedindo o uso de um código já
existente.
Podemos também incluir um método para pesquisar se um determinado código
existe na tabela, obtendo os valores do produto caso positivo ou deixando
tudo em branco caso contrário. Veja o código abaixo:
Public Sub Pesquisar(Valor As Long)
   
    Dim Produtos As Range
    Dim Linha As Long
    Set Produtos = plProdutos.Range(“tbProdutos”)
    Codigo = 0
    Nome = “”
    Quantidade = 0
    Preco = 0.01    ‘ O preço deve ser
sempre positivo
    For Linha = 2 To Produtos.Rows.Count
        If Produtos.Cells(Linha,
1).Value = Valor Then
            Codigo
= Produtos.Cells(Linha, 1).Value
            Nome =
Produtos.Cells(Linha, 2).Value

Quantidade = Produtos.Cells(Linha, 3).Value
            Preco
= Produtos.Cells(Linha, 4).Value
            Ativo
= Produtos.Cells(Linha, 5).Value
            Exit
For
        End If
    Next
End Sub
Antes de iniciar a pesquisa, as propriedades do objeto Produto são zerados, para que um eventual conteúdo anterior não interfira na
validação da pesquisa. A estrutura de repetição começa com valor 2 porque a primeira linha do
intervalo tbProdutos é o cabeçalho. Podemos criar um procedimento
para testar o funcionamento da pesquisa. Coloque o código abaixo no módulo
de teste:
Sub TestePesquisar()
    Dim Produto As clsProduto
    Set Produto = New clsProduto
    Produto.Pesquisar (3)
   
    If Produto.Codigo = 0 Then
        Debug.Print “O código ” &
Produto.Codigo & ” não foi encontrado”
    Else
        Debug.Print “Codigo: ” &
Produto.Codigo
        Debug.Print “Nome: ” &
Produto.Nome
        Debug.Print “Quantidade: ”
& Produto.Quantidade
        Debug.Print “Preco: ” &
Produto.Preco
    End If
End Sub
Crie vários produtos com valores diferentes e execute o procedimento. A
área de Verificação imediata mostrará se o produto existe ou não na
tabela. Caso exista, mostrará os valores das propriedades, caso contrário
dará uma mensagem informativa.
O método para alteração é similar ao de pesquisar. Com base nas
propriedades, podemos localizar o código e atualizar o restante das
propriedades com os valores atuais. Vejamos:
 
Public Sub Atualizar(Valor As Long)  
 
    Dim Produtos As Range
    Dim Linha As Long
    Set Produtos = plProdutos.Range(“tbProdutos”)
    For Linha = 2 To Produtos.Rows.Count
        If Produtos.Cells(Linha,
1).Value = Valor Then

Produtos.Cells(Linha, 2).Value = Nome

Produtos.Cells(Linha, 3).Value = Quantidade

Produtos.Cells(Linha, 4).Value = Preco
            Exit
For
        End If
    Next
End Sub
Para testar, vamos utilizar o seguinte procedimento:
Sub TesteAtualizar()
    Dim Produto As clsProduto
    Set Produto = New clsProduto
    Produto.Pesquisar (6)
   
    Produto.Quantidade = 25
    Produto.Preco = 18
   
    Produto.Atualizar (Produto.Codigo)
End Sub
Neste teste pesquisamos um produto, alteramos algumas propriedades e depois
atualizamos o mesmo produto para que a tabela reflita as alterações
desejadas. Depure para compreender o funcionamento.
Por fim, vamos criar um método para desativar o produto:
Public Sub Desativar(Valor As Long)
   
    Dim Produtos As Range
    Dim Linha As Long
    Dim Encontrado As Boolean
    Set Produtos = plProdutos.Range(“tbProdutos”)
    Encontrado = False
    For Linha = 2 To Produtos.Rows.Count
        If Produtos.Cells(Linha,
1).Value = Valor Then

Produtos.Cells(Linha, 5).Value = False

Encontrado = True
            Exit
For
        End If
    Next
   
    If Encontrado = False Then
        Err.Raise Number:=1003,
Description:=”Produto não encontrado”
    End If
End Sub
O procedimento irá procurar o código do produto e irá alterar o conteúdo da
célula Ativo para Falso. Se o produto não for encontrado uma
mensagem de erro aparecerá. Podemos testar o código com outro procedimento
de teste:
Sub TesteDesativar()
    Dim Produto As clsProduto
    Set Produto = New clsProduto
    Produto.Desativar (1)
End Sub
Execute o código e veja a alteração feita na tabela. Experimente também um
código não existente para ver a mensagem de erro. Se houver necessidade
pode-se criar um método para reativar um produto, o que você deve fazer com
muita facilidade.

Como utilizar essa classe que foi criada? Imagine um formulário de
produtos, onde podemos criar, consultar, alterar e excluir produtos. Os
eventos dos botões, onde estaria toda a lógica, pode fazer uma consistência
dos dados (conferir se os dados essenciais estão preenchidos e se são
válidos) para então chamar o método da classe que efetiva o cadastro ou a
alteração na tabela de produtos. Em um outro formulário que for preciso
obter o dado de algum produto, bastaria chamar o método da classe, sem
necessidade de replicar o código que estaria no formulário de
produtos.

As classes servem para encapsular um objeto e manter toda a inteligência
acerca dele. No início pode parecer um pouco complicado trabalhar com classes, mas com o
tempo você verá que pode facilitar muito a longo prazo. Da mesma forma que
formulários, módulos de classe podem ser exportados para ser importados por
outras planilhas e adaptados,  com poucas alterações, demandando menos tempo do que fazer todo o trabalho de novo.

Vimos até aqui como funcionam as propriedades das classes. Utilizamos
propriedades com e sem consistência no Property Let, bem como propriedades somente leitura e totalmente privada. Criamos
métodos para adicionar, pesquisar, alterar e desativar produtos, assim como
procedimentos para testar o funcionamento da classe. Também criamos um
método privado da classe. Acredito que este conteúdo apresentado dá uma boa
base para que você possa criar suas próprias classes.

Sempre que for criar uma classe crie também um módulo exclusivo para
testes, de forma que você possa verificar se suas propriedades e métodos
estão funcionando adequadamente, como fiz aqui. Manter um módulo separado
para testes evita confusão com o código que será de fato usado, que deverá
estar em outro módulo. Assim que estiver tudo testado e funcionando, o
módulo de testes pode ser excluído se quiser.
Deixe seu comentário informando se conseguiu absorver como funcionam as
classes no Excel. Diga se houve dificuldade de entender alguma coisa e se
vai começar a usar classes em projetos futuros.

Pedro Martins

 

Formado em Tecnologia em Eletrônica Digital, já trabalhou como
artefinalista, eletrotécnico, programador de CLP (para máquinas
industriais) e analista de sistemas em sistema bancário, programando em
COBOL.
Mexe com computadores e programação desde a segunda metade dos anos
1980, quando teve um MSX e aprendeu a programar em BASIC. É a favor da
disseminação do conhecimento.

 

 

Catálogo de aulas (NOVIDADE)
 
Criei um catálogo de aulas para ajudar você em seus estudos. Acesse clicando na imagem abaixo ou clique aqui.
 

 

 

 

 

Contato

Telefone:

+55 11 98861.4882

E-mail:

contato@alessandrotrovato.com.br

Siga-nos

Facebook Instagram Linkedin Twitter Youtube

© Copyright 2024 – Todos os direitos reservados | Alessandro Trovato

criado por: