Está en la página 1de 32

CURSO DE VB

CAPÍTULO 181

Índice de contenido
TIPOS, COLECCIONES Y CLASES..................................................................................................2
INTRODUCCIÓN...........................................................................................................................2
TIPOS..............................................................................................................................................2
COLECCIONES..............................................................................................................................6
DEFINIR UNA COLECCIÓN....................................................................................................6
AÑADIR ELEMENTOS A UNA COLECCIÓN........................................................................7
CONTAR ELEMENTOS DE UNA COLECCIÓN....................................................................7
ELIMINAR ELEMENTOS DE UNA COLECCIÓN.................................................................7
RECORRER LOS ELEMENTOS DE UNA COLECCIÓN.......................................................8
UN EJEMPLO CON TODO LO ANTERIOR............................................................................8
CLASES...........................................................................................................................................9
TRABAJANDO CON LAS CLASES...........................................................................................11
EXPORTANDO EL MÓDULO DE CLASE............................................................................12
SEGUIMOS CON EL TRABAJO CON CLASES...................................................................13
UTILIZANDO LAS PROPIEDADES: PROCEDIMIENTO PROPERTY..............................16
PROPERTY LET..................................................................................................................16
PROPERTY GET.................................................................................................................16
PROPERTY SET..................................................................................................................17
APLIQUEMOS EL PROCEDIMIENTO PROPERTY........................................................17
UNAS BREVES EXPLICACIONES TEÓRICAS BASADAS EN TODO LO ANTERIOR. 21
UN EJEMPLO INTEGRÁNDOLO CON ACCESS.....................................................................23
PARA FINALIZAR ESTE CAPÍTULO........................................................................................31

1 La BD donde están los ejemplos de este capítulo os la podéis bajar aquí.

1
Visítame en http://bit.ly/NckAccess
TIPOS, COLECCIONES Y CLASES

INTRODUCCIÓN
El objetivo de este capítulo es llegar a comprender las
clases. Sin embargo, para ello es necesario asimilar dos
conceptos clave: los tipos y las colecciones. Si bien no son
lo mismo (lógicamente) las ideas que subyacen en cada uno
de ellos nos allanarán el camino para llegar a las clases.

Es por esto por lo que empezaremos desarrollando los que son los tipos, cómo se definen, etc.,
y haremos lo mismo con las colecciones (que, además, ya nos debería “sonar de algo” si
hemos seguido todo este curso hasta aquí).

Como utilizaremos los conceptos explicados cuando hablábamos de matrices, si notáis que
tenéis un momento de “debilidad memorística”, quizá sería interesante que tuvierais a mano el
capítulo 9, donde se habla de ellas. 

Os advierto que este capítulo quizá pueda resultaros algo complejo, sobre todo en la parte
final del mismo y en especial en el apartado “Un ejemplo integrándolo con Access”,
básicamente porque se mezclan no sólo cosas de este capítulo sino elementos que hemos ido
explicando (y aprendiendo, supongo ) durante este curso. Pensad que la finalidad es
podernos construir nuestro propio módulo de clase (todo un “lujazo” para nuestra aplicación).

Empecemos pues.

TIPOS
Nuestro buen amigo Access nos dice, refiriéndose a la instrucción Type, lo siguiente: <<Se usa
en el nivel de módulo para definir un tipo de datos definido por el usuario que contiene uno o
más elementos>>.

Vayamos por partes:

• “Se usa a nivel de módulo”. Pues vaya, ya tenemos una primera pista.
• “Tipo de datos”. Sabemos que hay datos numéricos, booleanos, de texto... ¿Qué tipo de
datos será?
• “Definido por el usuario”. Ergo no es un tipo de datos llamémosle “estándar”, sino que
es el usuario el que se lo “saca de la manga”.
• “Contiene uno o más elementos”. Eso me suena a matrices, ¿verdad?

Sigamos. La estructura para definir un tipo es la siguiente:

PUBLIC/PRIVATE TYPE nombreTipo


nombreElemento1 AS tipoDato

nombreElementoN AS tipoDato
END TYPE

OK. Ya tenemos la estructura para definir un tipo. ¿Y los elementos?

2
Visítame en http://bit.ly/NckAccess
Pues los elementos deben ser definidos dentro de un procedimiento.

Lo anterior nos lleva a tener que remarcar dos puntos:

• La declaración del tipo NO va dentro de un procedimiento,


sino que debemos situarlo en la cabecera del módulo.
• La declaración de los elementos SÍ va dentro de un
procedimiento.

Vamos a realizar un pequeño ejercicio muy simple para


empezar a entender lo anterior. Después complicaremos un
poco el asunto.

En una BD en blanco vamos a insertar un módulo estándar, que llamaremos mdlDefinoTipos.


Vamos a crearnos un tipo personalizado, que llamaremos CodigoColor. La idea es que nuestra
empresa tiene, como mecanismo de seguridad, un código de colores: a cada color le
corresponde un grado de peligrosidad.

Como hemos comentado antes, la definición de los tipos debe ir en la cabecera del módulo
(debajo de Option Compare Database / Option Explicit). Así, los elementos que componen
nuestro tipo serán dos:

1.- Nombre del color


2.- Significado de peligro

Deberíamos pues definirlo así:


Public Type CodigoColor
nombre As String * 10
significado As String * 25
End Type

Como veis, las variables que indican la definición de los elementos son, en este caso, de tipo
texto String. Además, hemos indicado su longitud máxima (a través de “* 10” y “* 25”).

Segunda fase: rellenar los elementos.

Como es el primer ejemplo “fácil” sólo vamos a rellenar los datos de un color. Como
comentaba antes, una vez entendido lo que hace nuestro código pondremos un ejemplo más
complejo.

Para rellenar los elementos del tipo vamos a utilizar un procedimiento público, que llamaremos
rellenaCodigos. Lo escribiríamos así, inmediatamente debajo de nuestra definición de tipo
personalizado CodigoColor:


Public Sub rellenaCodigos()
'Declaramos una variable, diciéndole que es de nuestro tipo personalizado
Dim vColor As CodigoColor
'Rellenamos los valores
vColor.nombre = "Verde"
vColor.significado = "Sin peligro"

3
Visítame en http://bit.ly/NckAccess
'Mostramos los resultados
MsgBox vColor.nombre & vColor.significado, vbInformation, "DATOS"
End Sub

Fijaos en que:

Debo declarar la variable como mi tipo personalizado


Debo indicar a qué elemento me refiero, y rellenarlo,
utilizando la estructura: <variable.elemento = valor>

Si nos situamos en nuestro procedimiento y pulsamos F5


veremos qué nos muestra el MsgBox.

Compliquemos un poco más la cosa. Supongamos que tenemos un listado de países con sus
monedas. El listado que utilizaremos será el siguiente:

1- USA - Dólar
2- España - Euro
3- Japón - Yen

Nos crearemos, en nuestro módulo, un nuevo tipo, al que llamaremos Monedas, de la manera
siguiente:


Public Type tipoMoneda
nombre As String * 10
Moneda As String * 10
End Type

Y nos crearemos un procedimiento llamado usaMoneda, así:


Public Sub usaMoneda(ByVal indice As Integer)
'Declaramos una variable como nuestro tipo, pero en forma de matriz
Dim paisMoneda(1 To 3) As tipoMoneda
'Rellenamos los datos
paisMoneda(1).nombre = "USA"
paisMoneda(2).nombre = "España"
paisMoneda(3).nombre = "Japón"
paisMoneda(1).moneda = "Dólar"
paisMoneda(2).moneda = "Euro"
paisMoneda(3).moneda = "Yen"
'Devolvemos un MsgBox con la información
MsgBox "El país " & paisMoneda(indice).nombre & " usa la moneda " &
paisMoneda(indice).moneda, _
vbInformation, "RESULTADO"
End Sub

Finalmente, nos creamos un formulario en blanco y, en un botón de comando, escribimos el


siguiente código2:

2 El tema de control de errores os lo dejo para vosotros 

4
Visítame en http://bit.ly/NckAccess
Private Sub cmdProcedimientousaMoneda_Click()
'Declaramos las variables
Dim vCod As Variant
'Solicitamos al usuario un código de país
vCod = InputBox("Introduzca código del país (número
del 1 al 3)", "CÓDIGO PAÍS", "1")
'Llamamos al procedimiento usaMoneda()
Call usaMoneda(vCod)
End Sub

Pensad que la “gracia” del ejemplo está en entender la


mecánica de cómo funciona la definición de tipos
personalizados.

También pensad que podemos “reutilizar” nuestra definición de tipo para otras acciones (si lo
planificamos bien nos puede salir una definición de tipos bastante “universal”).

Por ejemplo, si creamos otro procedimiento para saber en qué moneda debemos pagar a un
cliente, sabiendo su código de cliente, sólo deberíamos escribir un procedimiento específico
para ello, sin tener que volver a definir otro tipo.

Es decir, en nuestro módulo podemos escribir lo siguiente:


Public Sub clienteMoneda(ByVal Indice As Integer)
Dim cliMon(1 To 3) As tipoMoneda
cliMon(1).nombre = "Meals Co"
cliMon(2).nombre = "Verduras SA"
cliMon(3).nombre = "Arigato"
cliMon(1).moneda = "Dólar"
cliMon(2).moneda = "Euro"
cliMon(3).moneda = "Yen"
MsgBox "Para " & cliMon(Indice).nombre & " debemos pagar usando el " &
cliMon(Indice).moneda, _
vbInformation, "MONEDA DE PAGO"
End Sub

Y el código de nuestro botón sería:


Private Sub cmdProcedimientoclienteMoneda_Click()
'Declaramos las variables
Dim vCod As Variant
'Solicitamos al usuario un código de cliente
vCod = InputBox("Introduzca código del cliente (número del 1 al 3)", "CÓDIGO CLIENTE",
"1")
'Llamamos al procedimiento clienteMoneda()
Call clienteMoneda(vCod)
End Sub

Para finalizar, tened en cuenta que hemos utilizado matrices estáticas para definir los índices
de los datos. No habría ningún problema en utilizar matrices dinámicas si no conocemos, de
antemano, el número de elementos que va a haber en la matriz.

5
Visítame en http://bit.ly/NckAccess
COLECCIONES
Durante todo el curso hemos estado viendo, en
determinados capítulos, distintas colecciones. A modo de
recordatorio, hemos visto la colección Fields() de un
recordset, o la colección Properties de los controles. En
definitiva, que si nos paramos a pensar un poco podremos
llegar a afirmar aquella frase de “en ocasiones veo
colecciones”.

¿Y qué es una colección? Nuestro amigo Access la define


así, añadiendo unos comentarios:

<<Un objeto Collection es un conjunto ordenado de elementos a los que se puede hacer
referencia como una unidad.

Comentarios
El objeto Collection permite hacer referencia de forma sencilla a un grupo de elementos
relacionados como un único objeto. Los elementos o miembros de una colección sólo tienen
que estar relacionados por el hecho de existir en la colección. Los miembros de una colección
no tienen que compartir el mismo tipo de dato>>.

Veamos:

• Es un conjunto ordenado de elementos → Ya sabemos que puede contener varios


elementos, de manera ordenada.
• Se puede hacer referencia a los mismos como una unidad → Para entendernos, yo
puedo tener un Porshe, un Mercedes, un Ferrari y un Lexus (por si alguien tenía dudas
esto es un ejemplo, y no tengo ninguno de los cuatro ). Pues bien, cuando quiero
referirme a todos ellos no los cito uno a uno, sino que digo “mis coches”. Luego,
“misCoches” es mi unidad, mi colección.
• Los elementos sólo tienen que estar relacionados por el hecho de existir en la colección
→ Es decir, si yo digo “misCoches”, ¿dónde queda mi Harley Davidson? No es un
elemento porque no he creado ninguna relación con la colección misCoches. Ahora bien,
si yo utilizo otra colección, y la llamo misMediosDeTransporte, ya hemos establecido
relación, por lo que ahora sí estoy citando misCoches y misMotos a la vez.
• Los miembros no tienen que compartir el mismo tipo de dato → Fácil: en mi colección
misMediosDeTransporte mi Lexus es un dato tipo “coche”, pero mi Suzuki es un dato
tipo “moto”, y mi “Boeing” es un dato tipo “avión”... y no olvidemos que sí están todos
en mi colección.

Con todo lo anterior ya vemos que podemos crearnos nuestras propias colecciones.

DEFINIR UNA COLECCIÓN


Para crear una colección debemos definirla como, precisamente, una colección, mediante la
palabra COLLECTION.

En definitiva, que si queremos crearnos la colección misCoches deberíamos declararla así:

Public misCoches As Collection

y definirla así:

Set misCoches = New Collection

6
Visítame en http://bit.ly/NckAccess
Nos debería sonar también la idea de que esos dos pasos podemos
resumirlos en uno, declarando la colección de la siguiente manera:

Public misCoches As New Collection

AÑADIR ELEMENTOS A UNA


COLECCIÓN
Para añadir elementos debemos utilizar la palabra ADD.

La estructura sería:

nomColección.Add “Elemento”

o bien

nomColeccion.Add “Elemento”, “Clave”

Por ejemplo, si yo quiero añadir mi coche “Porshe Carrera” a la colección escribiría lo siguiente:

misCoches.Add “Porshe Carrera”

Si, además, quisiera identificar ese coche con una palabra clave, podría añadirla también con
el ADD. Por ejemplo, supongamos que mi clave para el “Porshe Carrera” es “PorCar”; luego
escribiría:

misCoches.Add “Porshe Carrera”, “PorCar”

CONTAR ELEMENTOS DE UNA COLECCIÓN


Para contar los elementos de una colección utilizamos la palabra COUNT.

La estructura sería muy sencilla, como imagino que ya habremos intuido:

nomColeccion.Count

Es decir:

misCoches.Count

ELIMINAR ELEMENTOS DE UNA COLECCIÓN


Para eliminar elementos utilizamos la palabra REMOVE.

Los elementos de una colección tienen todos un índice, que vendría a ser el identificador de la
posición que ocupan en la colección. Sabiendo esto, y también sabiendo que podemos eliminar
un elemento por su palabra clave, la estructura para eliminar sería:

nomColeccion.Remove (índice)

o bien

7
Visítame en http://bit.ly/NckAccess
nomColeccion.Remove (“clave”)

Es decir, que si mi “Porshe Carrera” tuviera el índice 6, podría eliminarlo así:

misCoches.Remove (6)

o así

misCoches.Remove (“PorCar”)

RECORRER LOS ELEMENTOS DE UNA


COLECCIÓN
Para recorrer los elementos de una colección debemos utilizar el bucle FOR EACH...NEXT. En
este curso hemos visto en algunos ejemplos cómo se utiliza dicho bucle. Su estructura es:

FOR EACH <elemento> IN <coleccion>


'Código
NEXT <elemento>

Como vamos a ver un ejemplo en el siguiente apartado no insisto más aquí.

UN EJEMPLO CON TODO LO ANTERIOR


Vamos a realizar un pequeño ejercicio con todo lo que acabamos de aprender. Vamos a
crearnos un nuevo módulo, al que llamaremos mdlColeccionCoches.


Public Sub colecCoches()
'Declaramos las variables
Dim cocheBorrado As String
'Declaramos la colección
Dim misCoches As Collection
'Definimos la colección
Set misCoches = New Collection
'Declaramos el contenedor de elementos de la colección
Dim elCoche As Variant
'Añadimos los elementos de la colección
With misCoches
.Add "Porshe Carrera", "PorCar"
.Add "Ford Mustang", "ForMus"
.Add "Mazda RX5", "MazRx5"
.Add "Toyota Celica", "ToyCel"
End With
'Mostramos cuantos coches tenemos
MsgBox "Tengo " & misCoches.Count & " cochazos", vbExclamation, "#COCHES"
'Recorremos la colección
For Each elCoche In misCoches
MsgBox "Un coche es un " & elCoche, vbInformation, "MIS COCHES"
Next elCoche
'Cogemos el valor del coche que vamos a borrar
cocheBorrado = misCoches("PorCar")
'----------------------------------------------
'Esta expresión sería equivalente a la anterior
'cocheBorrado = misCoches.Item(1)

8
Visítame en http://bit.ly/NckAccess
'----------------------------------------------
'Borramos el primer coche
misCoches.Remove (1)
MsgBox "¡Oh! No recordaba que el primer coche lo vendí
por un pastón" _
& vbCrLf & vbCrLf & "Ahora sólo tengo " &
misCoches.Count & " 'cochecitos'", _
vbExclamation, "#COCHES"
MsgBox "Quiero decir, que el coche que vendí fue el " &
cocheBorrado, vbInformation, "VENDIDO"
End Sub

Y sólo nos queda, en cualquier formulario, añadirle este código a un botón de comando:


Private Sub cmdLlamaColecCoches_Click()
Call colecCoches
End Sub

Si analizáis el primer código veréis cómo he manipulado la colección y sus propiedades según
lo que os he estado explicando en apartados anteriores. No creo que tenga mayor dificultad
para nuestros ya entrenados cerebros 

Sólo quisiera llamaros la atención sobre algo que no habíamos comentado aún, y que es la
propiedad ITEM. Fijaos que cuando asigno valor a la variable <cocheBorrado> lo hago a través
de su clave, así:

cocheBorrado = misCoches("PorCar")

Pero si no tuviera clave, o mi “guía” fuese el índice del elemento en la colección, podría utilizar
esta otra expresión:

cocheBorrado = misCoches.Item(1)

para hacer referencia al elemento (item) número 1 de la colección.

CLASES
Nuestro amigo Access nos define una clase como <<Definición formal de un objeto. La clase
actúa como plantilla desde la que se crea una instancia de un objeto en tiempo de ejecución.
La clase define las propiedades del objeto y los métodos utilizados para controlar su
comportamiento>>.

Veamos:

• Define un objeto. Eso significaría que lo que nos permite crear son objetos, y en los
capítulos anteriores hemos visto algunas de las muchas cosas que se pueden hacer con
los objetos.

• Actúa como plantilla desde la que se crea una instancia de un objeto. Ya sabemos que
lo que nos estamos creando es una plantilla, y que gracias a esa plantilla vamos a crear
una instancia del objeto. ¿Y qué significa esto? Que nosotros no vamos a trabajar con la
plantilla directamente cuando creemos un objeto, sino que vamos a trabajar con una
instancia.

9
Visítame en http://bit.ly/NckAccess
Quizá lo anterior pueda resultar un poco confuso, pero un
ejemplo gráfico creo que nos ayudará: supongamos que yo
creo una clase que se llama “rectángulo”. Cuando necesito
un objeto “rectángulo” llamo a la clase y creo el objeto. En
realidad creo una instancia del objeto: todo el trabajo que
realice se hará sobre esa instancia, no sobre la plantilla. Es
decir:

• Se crea una instancia del objeto en tiempo de ejecución. Lo que significa, para
entendernos, que se crea “mientras tenemos Access en marcha”.

• La clase define las propiedades del objeto y los métodos utilizados para controlar su
comportamiento. Ya sabemos pues que el objeto tendrá propiedades y métodos, y que
podremos establecer sus valores para que la instancia se comporte según nuestros
deseos.

Aunque no hayamos sido conscientes con los ejemplos que hemos ido aprendiendo durante
este curso estábamos realizando este proceso. Por poner un ejemplo “fresco”, ¿recordamos el
capítulo dedicado al “Scripting Runtime”, en concreto el ejemplo de nuestro “diccionario”
(capítulo 17)?

Pues bien, cuando escribíamos:


'Declaramos las variables
Dim abv As Object
'Creamos el objeto abv
Set abv = CreateObject("Scripting.Dictionary")
'Añadimos los significados de las abreviaturas
abv.Add "DDCG", "Dar de comer al gato"

fncMiTip = abv.Item(vAbreviado)

Mediante la línea del <Set abv...> estábamos creando la

10
Visítame en http://bit.ly/NckAccess
instancia del objeto de la clase Scripting.Dictionary. La
estábamos creando, evidentemente, en tiempo de ejecución
(no “parábamos” Access para poder crear dicho objeto) y, a
continuación, manipulábamos, en este caso, un método de
la clase, que era ADD, y una propiedad, en este caso, ITEM.

Espero que con lo anterior tengamos meridianamente claro


qué es una clase y los elementos que la componen, porque
es básico para poder comprender lo que veremos en los
apartados siguientes 

TRABAJANDO CON LAS CLASES


Vamos a ver cómo podemos trabajar con una clase personalizada a través de un ejemplo.
Vamos a planificar primero las bases de nuestro proceso:

 La clase se llamará clsVehic, y nos recogerá los vehículos de que disponemos.


 Tendremos tres tipos de vehículos: coches, motos y camiones.
 Cada vehículo tendrá unas características determinadas, que recogeremos a través de
variables públicas.
 Con la información introducida, más información que introduciremos a futuro, en tiempo de
ejecución, nuestro módulo de clase realizará una serie de operaciones.

Voy a explicar el proceso relacionándolo con los puntos anteriores, para que podáis seguir
mejor el hilo de la explicación.

Para cumplir el punto  nos vamos al editor de VB y en el menú Insertar añadimos un módulo
de clase. Lo guardamos como clsVehic.

Para cumplir los puntos  y  vamos a definir las características que necesitaremos. Para ello
escribimos en nuestro módulo de clase lo siguiente:


Option Compare Database
Option Explicit

Public claseVehic As String 'Si es coche, moto o camión


Public nombre As String 'Nombre del vehículo (v.gr., Porshe)
Public tipo As String 'Tipo del vehículo (v.gr., Carrera)
Public cilindrada As Long 'Cilindrada
Public capacDep As Double 'Capacidad del depósito, en litros

Para cumplir con el punto  vamos a necesitar unas funciones que nos calcularan los
resultados. La primera función nos unirá el nombre con su tipo; la segunda función nos
calculará cuánto costaría llenar el depósito en el momento actual (es decir, que el proceso nos
pedirá el importe del litro de combustible en el momento en que necesitemos ese dato).

Así, a continuación de lo anterior escribimos estas dos funciones:


Public Function nomVehic() As String
nomVehic = nombre & " " & tipo
End Function

Public Function costeDeposito(ByVal precioLitro As Double) As Currency


costeDeposito = capacDep * precioLitro

11
Visítame en http://bit.ly/NckAccess
If costeDeposito > 150 Then
MsgBox "¡Qué caro! Mejor dejar el vehículo
aparcado!", vbExclamation, "UFFF"
End If
End Function

La primera función nos devuelve una simple concatenación


de valores, separados por un espacio en blanco. Sin
problemas.

En la segunda función se produce una operación matemática de multiplicación. He añadido,


además, que se lance un MsgBox en función del valor obtenido, simplemente para que veáis
que se pueden realizar varias “operaciones” en una misma función.

Ya tenemos pues nuestro módulo de clase creado con todas las características que
necesitábamos. Ese módulo de clase lo podríamos exportar para poder utilizarlo en diferentes
bases de datos que tuviéramos sobre vehículos.

EXPORTANDO EL MÓDULO DE CLASE


Haremos un inciso y aprovecharemos para explicar cómo exportar el módulo de clase. Nos
vamos para ello al VBE y seleccionamos el módulo clsVehic. Hacemos click con el botón
derecho y seleccionamos la opción “Exportar archivo...”

Se nos abrirá una ventana que nos pedirá dónde queremos guardarlo. Como es un módulo de
clase tendrá la extensión cls.

12
Visítame en http://bit.ly/NckAccess
Si quisiéramos realizar una importación de clase el proceso sería el mismo (situándonos en
cualquier espacio en blanco en la ventana de proyecto): haríamos click derecho y elegiríamos
la opción “Importar archivo...”

SEGUIMOS CON EL TRABAJO CON CLASES


Prosigamos. Lo explicado hasta ahora de clsVehic estoy seguro que nos recuerda a lo que, al
principio de este capítulo, explicábamos sobre “Definición de tipos”, ¿verdad? Pues ahora, para
poder continuar, vamos a pasar a utilizar nuestro siguiente elemento: las colecciones,
combinándolo esta vez con el módulo de clase.

En nuestro formulario de pruebas añadimos un botón de comando, que llamaremos


cmdListaVehiculos, que llamará al procedimiento misVehiculos(), que crearemos en breve. El
código de ese botón sería, simplemente:


Private Sub cmdListaVehiculos_Click()
Call misVehiculos
End Sub

Ahora sí nos creamos un módulo estándar, al que llamaremos mdlVehic. En él vamos a


crearnos una colección con nuestros vehículos. Así pues, escribiremos el siguiente código 3 (ojo
con el código, que es “intenso” ):


Public Sub misVehiculos()
'Definimos la colección
Set colMisVehic = New Collection
'Declaramos un objeto perteneciente a nuestra clase
Dim unVehiculo As clsVehic
'Declaramos una variable que recogerá los elementos de la colección, y
'que también pertenecerá a nuestra clase
Dim elemVehic As clsVehic
'Creamos una instancia del objeto unVehiculo
Set unVehiculo = New clsVehic
'Rellenamos los datos que necesitamos
With unVehiculo
.claseVehic = "Coche"
.nombre = "Porshe"
.tipo = "Carrera"

3 No tengo ni idea de las características técnicas de los vehículos que voy a introducir. De hecho es posible que introduzca alguna
“salvajada”. En definitiva, que me lo voy inventando a medida que escribo. Por ello, no toméis estos valores como referencias
reales.

13
Visítame en http://bit.ly/NckAccess
.cilindrada = 3000
.capacDep = 80.5
End With
'Lo añadimos a nuestra colección
colMisVehic.Add unVehiculo, "PorCar"
'Rellenamos el siguiente vehículo
Set unVehiculo = New clsVehic
With unVehiculo
.claseVehic = "Coche"
.nombre = "Mercedes"
.tipo = "CLK"
.cilindrada = 2800
.capacDep = 85.7
End With
colMisVehic.Add unVehiculo, "MerCLK"
'Rellenamos el siguiente vehículo
Set unVehiculo = New clsVehic
With unVehiculo
.claseVehic = "Moto"
.nombre = "Suzuki"
.tipo = "Bandid"
.cilindrada = 650
.capacDep = 30.3
End With
colMisVehic.Add unVehiculo, "SuzBan"
'Rellenamos el siguiente vehículo
Set unVehiculo = New clsVehic
With unVehiculo
.claseVehic = "Moto"
.nombre = "Ducati"
.tipo = "Storm"
.cilindrada = 1200
.capacDep = 46.2
End With
colMisVehic.Add unVehiculo, "DucSto"
'Rellenamos el siguiente vehículo
Set unVehiculo = New clsVehic
With unVehiculo
.claseVehic = "Camión"
.nombre = "Mercedes"
.tipo = "Pegaso"
.cilindrada = 4000
.capacDep = 120.7
End With
colMisVehic.Add unVehiculo, "MerPeg"
'Ya podemos mostrar la información de la colección a petición del usuario
'No comento esta parte de código porque deberíamos entenderla ya perfectamente
Dim vClaseVehic As Variant
Dim vPrecioCombustible As Variant
Peticion:
vClaseVehic = InputBox("Seleccione qué información desea: 1-Coches; 2-Motos;" _
& " 3-Camiones; 0-Todos", "INFORMACIÓN VEHÍCULOS", 0)
If StrPtr(vClaseVehic) = 0 Then GoTo BorraColeccion
If Not IsNumeric(vClaseVehic) Then
MsgBox "El valor introducido no es válido", vbCritical, "INCORRECTO"
GoTo Peticion
End If
If vClaseVehic < 0 Or vClaseVehic > 3 Then
MsgBox "El valor introducido no es válido", vbCritical, "INCORRECTO"
GoTo Peticion
End If
'Por simplificar suponemos que todos utilizan el mismo tipo de combustible
vPrecioCombustible = InputBox("Introduzca el precio por litro de combustible", "PRECIO")
'También, por simplificar, no introduzco elementos de control para el valor introducido
'Si quisiérais utilizarlos os podéis guiar por los elementos de control que he utilizado
'en el primer InputBox
If StrPtr(vPrecioCombustible) = 0 Then GoTo BorraColeccion
Select Case vClaseVehic
Case 0
For Each elemVehic In colMisVehic
MsgBox "El vehículo tiene las siguientes características:" & vbCrLf & vbCrLf _

14
Visítame en http://bit.ly/NckAccess
& "Clase: " & elemVehic.claseVehic & vbCrLf _
& "Nombre: " & elemVehic.nomVehic & vbCrLf _
& "Cilindrada: " & elemVehic.cilindrada & vbCrLf _
& "Capacidad del depósito: " & elemVehic.capacDep & vbCrLf _
& "Precio de llenado del depósito: " & elemVehic.costeDeposito(vPrecioCombustible), _
vbInformation, "DATOS"
Next elemVehic
Case 1
For Each elemVehic In colMisVehic
If elemVehic.claseVehic = "Coche" Then
MsgBox "El vehículo tiene las siguientes características:" & vbCrLf & vbCrLf _
& "Clase: " & elemVehic.claseVehic & vbCrLf _
& "Nombre: " & elemVehic.nomVehic & vbCrLf _
& "Cilindrada: " & elemVehic.cilindrada & vbCrLf _
& "Capacidad del depósito: " & elemVehic.capacDep & vbCrLf _
& "Precio de llenado del depósito: " & elemVehic.costeDeposito(vPrecioCombustible), _
vbInformation, "DATOS"
End If
Next elemVehic
Case 2
For Each elemVehic In colMisVehic
If elemVehic.claseVehic = "Moto" Then
MsgBox "El vehículo tiene las siguientes características:" & vbCrLf & vbCrLf _
& "Clase: " & elemVehic.claseVehic & vbCrLf _
& "Nombre: " & elemVehic.nomVehic & vbCrLf _
& "Cilindrada: " & elemVehic.cilindrada & vbCrLf _
& "Capacidad del depósito: " & elemVehic.capacDep & vbCrLf _
& "Precio de llenado del depósito: " & elemVehic.costeDeposito(vPrecioCombustible), _
vbInformation, "DATOS"
End If
Next elemVehic
Case 3
For Each elemVehic In colMisVehic
If elemVehic.claseVehic = "Camión" Then
MsgBox "El vehículo tiene las siguientes características:" & vbCrLf & vbCrLf _
& "Clase: " & elemVehic.claseVehic & vbCrLf _
& "Nombre: " & elemVehic.nomVehic & vbCrLf _
& "Cilindrada: " & elemVehic.cilindrada & vbCrLf _
& "Capacidad del depósito: " & elemVehic.capacDep & vbCrLf _
& "Precio de llenado del depósito: " & elemVehic.costeDeposito(vPrecioCombustible), _
vbInformation, "DATOS"
End If
Next elemVehic
End Select
'Borramos los elementos de la colección
BorraColeccion:
Dim i As Long
For i = colMisVehic.Count To 1 Step -1 'Realizamos una cuenta atrás
colMisVehic.Remove (i)
Next i
End Sub

Si vemos “desde arriba” el código veremos claramente (¡eso espero!) su estructura:

1.- Definimos las colecciones y los objetos, y los relacionamos, mediante dicha definición, con
el módulo de clase que hemos creado.
2.- Rellenamos los datos del elementos y lo añadimos a la colección.
3.- Repetimos el proceso de rellenado con todos los elementos que queramos.
4.- Solicitamos la información al usuario
5.- En función de lo que quiere ver el usuario (coche, moto o camión), para lo cual utilizamos
un SELECT CASE...END SELECT, recorremos los elementos de la colección a través de un
bloque FOR EACH...NEXT
6.- En dicho bloque operamos con las propiedades de la clase que hemos creado (es decir, las
variables de nuestro tipo) y con los métodos (es decir, con las dos funciones que habíamos
definido en el módulo de clase). En este caso la operación es mostrar un MsgBox con la
información pertinente.

15
Visítame en http://bit.ly/NckAccess
7.- Finalizado el proceso, borramos todos los elementos de la colección para dejarla vacía.

Si hubiéramos querido incluso eliminar la colección en sí


hubiéramos añadido, justo antes del “End Sub”, la línea de
código:

Set colMisVehic = Nothing

¿Fácil, no? Je, je... (Ánimo, que con un poco de práctica


esto está hecho!).

UTILIZANDO LAS PROPIEDADES: PROCEDIMIENTO PROPERTY


Y ahora que os he “machacado” con un montón de código es cuando os digo: “vamos a
modificarlo con una cosa nueva” 

La pregunta que nos podría dar origen a esta explicación es: ¿qué pasa si el valor asignado a
capacidad del depósito de combustible no es correcta? Es decir, que, por error, introducimos
una capacidad de 10000 litros (que yo sepa, no hay ningún vehículo, dentro de los tipos de
vehículos sobre los que estamos trabajando, que pueda tener un depósito así: ¡habría más
depósito que vehículo!).

Para realizar un control de este elemento podríamos utilizar el procedimiento PROPERTY

Para que nos entendamos, y hablando en términos generales, un procedimiento PROPERTY


vendría a ser un procedimiento que gestiona las características de una propiedad de nuestra
clase y, por extensión, gestiona los errores (en el sentido de “introducciones erróneas de
valor”) que pudieran producirse.

También nos permite que una propiedad de una clase adopte un comportamiento determinado
en función de los parámetros que reciba.

PROPERTY LET
Mediante la instrucción PROPERTY LET generamos el código encargado de gestionar las
características de la propiedad del elemento definido en la clase.

En abstracto, lo que haría esta instrucción sería:


Public Property Let …
'Si pasa esto haz esto
'Si pasa aquello haz esto otro
'Si pasa eso haz eso otro
End Property

Como vemos, podríamos equipararlo a un procedimiento SUB por su estructura.

PROPERTY GET
Para poder obtener el valor asignado a la propiedad utilizaremos la instrucción PROPERTY GET.

16
Visítame en http://bit.ly/NckAccess
Hablando en abstracto, podríamos comparar un PROPERTY GET con una función, dado que
devuelve un valor.

Su estructura sería:


Public Property Get nombre(argumentos) As <tipo>
'Codigo
End Property

PROPERTY SET
Podemos asignar una propiedad mediante el property set. En este caso me remitiré a la ayuda
de Access, que indica que utilizamos el PROPERTY SET para asignar una referencia a un objeto.

Podríamos hacerlo siguiendo la siguiente estructura:


Public Property Set nombre(variableX As Objeto)
Set variableY = variableX
End Property

APLIQUEMOS EL PROCEDIMIENTO PROPERTY


A efectos de ser más claro voy a realizar de nuevo todo el proceso anterior, bautizándolo
ahora, para que nos entendamos, como clsVehic2. Si queréis modificar vuestro código anterior,
pues perfecto, y si queréis crearlo de nuevo, pues también perfecto: así practicaremos y
afianzaremos el estudio de las clases.

Manos a la obra...

Vamos a suponer que no hay vehículo (de los nuestros) que pueda tener un depósito superior
a los 500 litros. Aprovecharemos para comprobar también que no se nos haya “colado” un
depósito con capacidad negativa. El resto de elementos del código serán los mismos.

Creamos un módulo de clase, y lo llamaremos clsVehic2.

En ese nuevo módulo de clase escribimos el siguiente código (ojo, marco en negrita lo nuevo;
el resto es como ya lo habíamos escrito en el ejemplo del apartado anterior):


Option Compare Database
Option Explicit

Const capacidadMax As Double = 500 'Defino la capacidad máxima


Private p_capacDep As Double 'Nueva variable que hace referencia a la propiedad
'que gestionaremos con Property Let. Ojo que es <Private>
Public claseVehic As String 'Si es coche, moto o camión
Public nombre As String 'Nombre del vehículo (v.gr., Porshe)
Public tipo As String 'Tipo del vehículo (v.gr., Carrera)
Public cilindrada As Long 'Cilindrada

Public Property Let capacDep(ByVal capacidad As Double)


'Analizamos los supuestos

17
Visítame en http://bit.ly/NckAccess
Select Case capacidad
'Si la capacidad es superior a 500 litros
Case Is > capacidadMax
Err.Raise 666, "clsVehic2", "La capacidad del depósito de
combustible no es real"
'Si la capacidad es negativa
Case Is < 0
Err.Raise 667, "clsVehic2", "La capacidad del depósito no
puede ser menor que cero"
Case Else
'Si no el valor es considerado correcto
p_capacDep = capacidad
End Select
End Property

Public Property Get capacDep() As Double


capacDep = p_capacDep
End Property

Public Function nomVehic() As String


nomVehic = nombre & " " & tipo
End Function

Public Function costeDeposito(ByVal precioLitro As Double) As Currency


costeDeposito = capacDep * precioLitro
If costeDeposito > 150 Then
MsgBox "¡Qué caro! Mejor dejar el vehículo aparcado!", vbExclamation, "UFFF"
End If
End Function

Fijémonos en las principales diferencias:

• La propiedad capacDep ahora ya no es una variable (Public capacDep As Double


'Capacidad del depósito, en litros), sino que viene gestionada por nuestro procedimiento
Property (Public Property Let capacDep(ByVal capacidad As Double))

• La variable que nos hace de “puente” entre Property Let y Property Get la hemos
llamado p_capacDep, pero, atención, la hemos definido como Private, para que su
ámbito de actuación sea sólo a nivel de clase (es decir, que no es accesible desde otros
módulos).

• La operación “puente” es la siguiente:


◦ Property Let gestiona el valor de la variable <capacidad>, pasada como argumento
en el propio procedimiento ((ByVal capacidad As Double)). Si no hay motivo de
error p_capacDep toma el valor de <capacidad> ('Si no el valor es considerado
correcto
p_capacDep = capacidad)
◦ Property Get asigna el valor de nuestra propiedad de clase a p_capacDep, de
manera que si esta tiene valor significa que todas las comprobaciones han sido
correctas (capacDep = p_capacDep)

Y fijémonos también que, si se incumple alguna de las condiciones, generamos nuestros


propios errores a través de4

• Err.Raise 666, "clsVehic2", "La capacidad del depósito de combustible no es real"


• Err.Raise 667, "clsVehic2", "La capacidad del depósito no puede ser menor que cero"

Sigamos: debemos crearnos un nuevo módulo estándar para generar la colección que nos dará
la información de nuestros vehículos. A este nuevo módulo lo llamaremos mdlVehic2. Para
simplificar sólo rellenaremos un vehículo por tipo y los listaremos, sin pedir otra cosa al
4 Para “refrescar memorias” podéis consultar el capítulo 13 de este curso

18
Visítame en http://bit.ly/NckAccess
usuario que el precio del combustible.

El código de ese nuevo módulo debería ser:


Option Compare Database
Option Explicit

'Declaramos una colección


Public colMisVehic As Collection

Public Sub misVehiculos2()


'Definimos la colección
Set colMisVehic = New Collection
'Declaramos un objeto perteneciente a nuestra clase
Dim unVehiculo As clsVehic2
'Declaramos una variable que recogerá los elementos de la colección, y
'que también pertenecerá a nuestra clase
Dim elemVehic As clsVehic2
'Creamos una instancia del objeto unVehiculo
Set unVehiculo = New clsVehic2
'Rellenamos los datos que necesitamos
With unVehiculo
.claseVehic = "Coche"
.nombre = "Porshe"
.tipo = "Carrera"
.cilindrada = 3000
.capacDep = 80.5
End With
'Lo añadimos a nuestra colección
colMisVehic.Add unVehiculo, "PorCar"
'Rellenamos el siguiente vehículo
Set unVehiculo = New clsVehic2
With unVehiculo
.claseVehic = "Moto"
.nombre = "Suzuki"
.tipo = "Bandid"
.cilindrada = 650
.capacDep = 30.3
End With
colMisVehic.Add unVehiculo, "SuzBan"
'Rellenamos el siguiente vehículo
Set unVehiculo = New clsVehic2
With unVehiculo
.claseVehic = "Camión"
.nombre = "Mercedes"
.tipo = "Pegaso"
.cilindrada = 4000
.capacDep = 120.7
End With
colMisVehic.Add unVehiculo, "MerPeg"
'Por simplificar suponemos que todos utilizan el mismo tipo de combustible
Dim vPrecioCombustible As Variant
vPrecioCombustible = InputBox("Introduzca el precio por litro de combustible", "PRECIO")
'También, por simplificar, no introduzco elementos de control para el valor introducido
'Si quisiérais utilizarlos os podéis guiar por los elementos de control que he utilizado
'en el primer InputBox
If StrPtr(vPrecioCombustible) = 0 Then GoTo BorraColeccion
'Procedemos a la enumeración de los vehículos
For Each elemVehic In colMisVehic
MsgBox "El vehículo tiene las siguientes características:" & vbCrLf & vbCrLf _
& "Clase: " & elemVehic.claseVehic & vbCrLf _
& "Nombre: " & elemVehic.nomVehic & vbCrLf _
& "Cilindrada: " & elemVehic.cilindrada & vbCrLf _
& "Capacidad del depósito: " & elemVehic.capacDep & vbCrLf _
& "Precio de llenado del depósito: " & elemVehic.costeDeposito(vPrecioCombustible), _
vbInformation, "DATOS"
Next elemVehic
'Borramos los elementos de la colección

19
Visítame en http://bit.ly/NckAccess
BorraColeccion:
Dim i As Long
For i = colMisVehic.Count To 1 Step -1 'Realizamos una cuenta atrás
colMisVehic.Remove (i)
Next i
End Sub

Si finalmente creamos en un formulario un botón de


comando deberemos asignarle una llamada al
procedimiento misVehiculos2 con este simple código:


Private Sub cmdListaVehicProperty_Click()
Call misVehiculos2
End Sub

Si hacemos click sobre ese botón (con el formulario en vista formulario) se nos solicitará el
precio del litro de combustible y nos enumerará, a través de MsgBox, nuestros vehículos con
sus características.

Si ahora modificamos la capacidad del depósito de nuestro Porshe (la idea es que nos
equivocamos involuntariamente al determinar dicha capacidad) y escribimos:


With unVehiculo
.claseVehic = "Coche"
.nombre = "Porshe"
.tipo = "Carrera"
.cilindrada = 3000
.capacDep = 800.5
End With

la ejecución de nuestro código nos devolverá lo siguiente:

De la misma manera, si escribimos


.capacDep = -80.5

Obtendremos el siguiente mensaje:

20
Visítame en http://bit.ly/NckAccess
UNAS BREVES EXPLICACIONES TEÓRICAS BASADAS EN TODO LO
ANTERIOR
Para profundizar en el marco teórico en base al ejemplo anterior vamos a ponernos metafísicos
y vamos a hablar del alma y del cuerpo. Me vais a permitir esta pequeña licencia porque me
viene “que ni pintada” para que podamos entender lo que os quiero transmitir. Además, ¿quién
os iba a decir que la religión serviría para explicar VBA?5

Supongamos que existe una “esencia universal” (llamémosle Dios, el Uno, el Todo...), y
supongamos que una persona humana, al nacer, está compuesto por un cuerpo físico y un
alma que, en realidad, es una parte de dicha “esencia universal”. Es decir, que dicha alma es
individualizable pero al mismo tiempo es parte de un Todo.

Pues bien, nuestra clase sería esa “esencia universal”. A partir de ahí podemos crear las
“personas” (=objetos) que necesitemos. Si yo creo el objeto <miVehiculo> y creo el objeto
<tuVehiculo> el “cuerpo físico” de <miVehiculo> es diferente y diferenciable de <tuVehiculo>,
pero sus almas, aunque diferentes, son partes de una “esencia universal” (= clase).

<miVehiculo> y <tuVehiculo> se denominan, en jerga informática, ejemplares de clase.

Bajemos un nivel: mi código VB y yo tomamos una gran decisión: vamos a tener un hijo. A
este hijo le vamos a poner de nombre <elHijoVehículo>.

La pregunta que nos hacemos en este punto es: ¿existe <elHijoVehículo>? Y la respuesta es:
sí existe “a nivel mental”, porque mi código y yo así lo hemos decidido, pero aún no existe a
nivel físico porque aún no ha sido concebido.

Y por fin vamos a poner sobre la mesa y en conjunción todo este planteamiento con lo que
acabamos de programar en el ejemplo anterior:

1.- Creamos la “esencia universal” → Eso se refleja en la creación de la clase (creación del
módulo de clase).

2.- Decidimos tener un hijo → Eso se refleja en la declaración de la variable.

Dim elHijoVehiculo As clsVehic

En este momento <elHijoVehiculo> no es nada. Siguiendo el símil, es “la expresión de un


deseo”.

5 Os ruego que toméis esta explicación únicamente como lo que es: una explicación. En ningún momento pretendo entrar en
valoraciones, ni críticas, ni desprecios, ni comparaciones. Al contrario, estos temas merecen todo mi respeto. Por favor, que nadie
le busque “cinco pies al gato” porque lejos de mí está actuar con un ánimo negativo, sino ser lo más claro posible en la explicación.

21
Visítame en http://bit.ly/NckAccess
3.- Concebimos <elHijoVehículo> → Eso se produce cuando hacemos

Set elHijoVehiculo = New clsVehic

Pero mucho cuidado: si en ese momento nos mezclan


nuestro recién nacido con otros bebés no vamos a ser
capaces de decir “¡Este es el mío!”. Nos hace falta “algo”,
¿verdad?

4.- Nos fijamos en los rasgos de nuestro “hijo” para saber


que es nuestro → Eso se produce cuando definimos las
propiedades (me vais a permitir la licencia de inventarme
dichas propiedades simplemente para que “queden mejor”
en el ejemplo).

With elHijoVehiculo
.Pelo = “Pelón”
.Nariz = “Como la del padre”
.Peso = “3 Kg”
.Lloro = “Muy llorón”
End With

En definitiva, que a través de todo lo anterior ya deberíamos tener muy claro todo el proceso
y, si nos falla algo, tendremos una “guía de pasos” para “detectar” en qué fase podría estar el
error.

Sigamos un poco más. Si os fijáis en el código, en la parte “repetitiva” con el comentario


'Rellenamos el siguiente vehículo
Set unVehiculo = New clsVehic
With unVehiculo

Veréis que en cada adición estamos escribiendo la línea de código:

Set unVehiculo = New clsVehic

¿Por qué es necesario escribirla?

Imaginemos que yo tengo una clase, clsBebe, y defino un ejemplar de clase así:

Set miBebe = New clsBebe

y le asigno valores a las propiedades:

With miBebe
.Caracter = “Dulce”
.Ojos = “Llorones”
End with

Ya tenemos a miBebe definido. A continuación creo otro miBebe:

Caso A: rellenando directamente las propiedades (sin el Set miBebe = New clsBebe)

With miBebe

22
Visítame en http://bit.ly/NckAccess
.Caracter = “Gruñón”
.Ojos = “Desafiantes”
End with

Caso B: rellenando las propiedades pero antes pasando por


el Set

Set miBebe = New clsBebe


With miBebe
.Caracter = “Gruñón”
.Ojos = “Desafiantes”
End with

¿Qué hemos obtenido en realidad según el caso?

Caso A → NO hemos obtenido otro bebé, sino que lo que hemos hecho es mostrar mi bebé en
su adolescencia

Caso B → Sí hemos obtenido otro bebé.

Es decir, que en el caso A no hemos creado uno nuevo, sino que, en el mismo bebé, hemos
cambiado sus propiedades.

En el caso B teníamos un bebé; a continuación este ya ha dejado de existir para ser sustituido
por otro miBebe, con otras propiedades diferentes.

¿Vemos la diferencia?

Para mayor abundamiento os propongo que en el código anterior (en el procedimiento “Public
Sub misVehiculos”) convirtáis en comentarios todos los <Set unVehiculo = New clsVehic>,
exceptuando el primero, y ejecutéis el código. Así podréis ver qué grata sorpresa nos llevamos.

UN EJEMPLO INTEGRÁNDOLO CON ACCESS


Desarrollemos un ejemplo con todo lo anterior, integrándolo con información que tengamos
almacenada en Access. En este ejemplo no utilizaré las colecciones porque creo que con lo
explicado anteriormente deberíamos tenerlo claro. Digamos que “le daré un enfoque diferente”.

Dado que quizá ya entremos en terrenos algo complejos os recomiendo que no sólo analicéis
los códigos que vendrán, sino también cómo se realiza el planteamiento del problema. Ni que
decir tiene que si el código es técnicamente perfecto pero el problema no está bien planteado y
planificado los resultados no van a ser satisfactorios.

He aquí el problema:

<<Nuestra empresa es una empresa de alquiler de coches. En nuestra flota de automóviles


tenemos una serie de modelos que se adaptan a unas características que nosotros hemos
generalizado. La idea es que nuestro cliente que venga a alquilar un coche pueda elegir una
combinación de esas características y, en un momento, podamos darle a escoger una serie de
vehículos que cumplan dichas características, a la vez que le proporcionamos información
complementaria actualizada que puede ser de mucho interés>>

Vayamos analizando los elementos de que disponemos al tiempo que creamos nuestras tablas
en Access.

En primer lugar, crearemos una tabla que nos recogerá los modelos y las características que

23
Visítame en http://bit.ly/NckAccess
necesitamos. A esa tabla la llamaremos TAutos. Tendrá la siguiente estructura (algunos
campos se podrían haber desglosado en dos o más, pero los he unificado a efectos de
simplificar al máximo y no hacer el ejemplo engorroso en demasía):

Fijaos que para los campos [GamaAuto], [TerrenoAuto] y [TipoCombAuto] lo ideal hubiera sido
crearnos tablas auxiliares con la información, y que nuestra TAuto hubiera “chupado” los datos
de dichas tablas auxiliares. Sin embargo, a efectos de simplificación, nosotros escribiremos los
valores directamente.

Apunto lo anterior para que lo tengáis en cuenta de cara a vuestras BD's.

Rellenamos TAutos con algunos datos6; por ejemplo, así:

Hagamos notar que el consumo medio se refiere al consumo en litros cada 100 kilómetros (por
si no quedaba claro qué significaba ese valor).

Avancemos un poco los acontecimientos:

• Nuestro cliente va a decidir qué gama de coche desea → Recurriremos a [GamaAuto]


• Nuestro cliente va a decidir en qué tipo de terreno va a utilizar el coche → Recurriremos
a [TerrenoAuto]
• Nuestro cliente nos va a informar de cuántos kilómetros prevé realizar → Sabemos qué
combustible utiliza el coche ([TipoCombAuto]) y cuál es su consumo medio por
kilómetro ([ConsumMedAuto]) → Recurriremos a una tabla auxiliar donde informaremos
del precio por litro de combustible, y algún trabajador nuestro actualizará la tabla en
cuanto se produzcan modificaciones en dichos precios.
• El precio de alquiler incluye ya seguro. Sin embargo, ofreceremos al cliente la
posibilidad de efectuar el pago de una sobreprima de seguro, que irá en función de la
gama del auto, y que ofrecerá coberturas extras (que la política de empresa decidirá en
función de lo que haya negociado con la compañía aseguradora) → Requeriremos de
una tabla auxiliar que nos relacione la gama con el precio de la sobreprima.

6 Los datos que veréis son totalmente inventados. No toméis estos datos como datos “de la vida real”.

24
Visítame en http://bit.ly/NckAccess
• El precio del alquiler irá en función de la gama del vehículo y de los días de alquiler.
Nuestro cliente nos indicará cuántos días va a necesitar el vehículo → Requeriremos de
una tabla auxiliar que nos dé los precios por día, según la gama, y que un trabajador
nuestro actualizará tan pronto como la empresa decida modificar esos
precios.

Con lo anterior ya podemos empezar a vislumbrar qué


necesidades tenemos, ¿verdad? Empecemos pues por
crearnos nuestras tablas auxiliares.

Crearemos la tabla TPrecioComb, que tendrá la siguiente


estructura:

Y la rellenaremos con los siguientes datos:

Crearemos la tabla TPrimas con la siguiente estructura:

Y la rellenaremos con los siguientes datos:

Crearemos la tabla TPrecios con la siguiente estructura:

Y rellenaremos los datos así:

25
Visítame en http://bit.ly/NckAccess
Espero que, hasta aquí, no nos hayamos perdido... je, je...

Vamos a la siguiente fase, que será crearnos la clase.


Insertamos pues un nuevo módulo de clase que llamaremos
clsAutos.

Veréis que he programado un procedimiento PROPERTY para comprobar que no pueda haber
un consumo medio negativo o igual a cero.
Eso lo podría haber controlado a través del campo [ConsumMedioAuto] de la tabla TAutos, con
una regla de validación, pero como la idea es practicar las clases lo controlaremos desde aquí.

Las explicaciones están como comentarios en el propio código.

Y, por fin, el código de clsAuto nos queda así:


Option Compare Database
Option Explicit

'Declaramos las variables


Private p_consumMedio As Double
Public nombreCoche As String
Public gamaCoche As String
Public terrenoCoche As String
Public tipoCombCoche As String

'Controlamos que el consumo medio no sea negativo ni cero


Public Property Let consumMedioCoche(ByVal consumoMedio As Double)
'Analizamos los supuestos
If consumoMedio <= 0 Then
Err.Raise 666, "clsAuto", "El consumo medio no puede ser negativo o cero"
Else
p_consumMedio = consumoMedio
End If
End Property

'Obtenemos el valor del valor puente ya depurado


Public Property Get consumMedioCoche() As Double
consumMedioCoche = p_consumMedio
End Property

'Establecemos el coste del alquiler según los días requeridos


Public Function alquilerCoche(numDias As Integer, precioDiario As Currency) As Currency
alquilerCoche = numDias * precioDiario
End Function

'Establecemos el coste de la sobreprima


Public Function sobreprimaCoche(importePrima As Currency, numDias As Integer) As Currency
sobreprimaCoche = (importePrima * numDias) * 10 / 365
End Function

'Establecemos el coste previsto en gasolina


Public Function gasolinaCoche(numKilometros As Long, precioComb As Currency) As Currency
gasolinaCoche = (p_consumMedio / 100) * numKilometros * precioComb
End Function

Como vemos, nuestra clase recogerá toda la información que queremos proporcionar al cliente.

26
Visítame en http://bit.ly/NckAccess
Para clarificar ideas digamos que:

• El procedimiento Property (Let y Get) comprueba


consumMedioCoche
• alquilerCoche nos proporciona el precio total del alquiler
• sobreprimaCoche nos proporciona el importe de esa prima
adicional. Obviamente la fórmula me la he inventado, no es
ninguna “receta mágica” 
• gasolinaCoche nos proporciona el consumo en función del
consumo por kilómetro, del número de kilómetros y el
precio por litro de carburante.

Siguiente fase: creemos un módulo estándar. A ese módulo estandar lo llamaremos mdlAuto.
¿Qué va a hacer ese módulo? Lo que va a hacer es un doble proceso, adornado con algunas
operaciones adicionales:

Uno: va a recoger toda la información de nuestra tabla TAutos y nos va a rellenar los datos
correspondientes a la clase

Dos: va a plasmar esos datos en una tabla temporal, para tener una base donde filtrar según
las necesidades del cliente y a través de la cual podremos preparar un informe para
entregárselo a nuestro seguro consumidor.

El código de ese módulo será el siguiente:


Option Compare Database
Option Explicit

'Requiere tener registrada la referencia "Microsoft DAO 3.6 Object Library" o


'módulo equivalente----------------------------------------------------------

'Declaramos las variables del módulo


Private Const nomTablaAux As String = "TAuxInfoCliente"
Private dbs As DAO.Database
Private rstIN As DAO.Recordset
Private rstOUT As DAO.Recordset
Private tbl As Object
Private miSql As String
Private miCoche As clsAuto
Private buscoPrecioComb As Currency
Private buscoPrima As Currency
Private buscoPrecioAlquiler As Currency

'Creamos el procedimiento que nos generará la tabla


Public Sub creoTAux(numKm As Long, diasAlq As Integer)

'Creo un control de errores por si se produce algún error inesperado


On Error GoTo sol_err

'Defino la BD de trabajo
Set dbs = CurrentDb
'En primer lugar debemos comprobar si la tabla ya existe. Si existe debemos
'borrarla para que no nos dé error de código
For Each tbl In CurrentData.AllTables
If tbl.Name = nomTablaAux Then
DoCmd.DeleteObject acTable, nomTablaAux
Exit For
End If
Next tbl
'Creamos la estructura de la tabla a través de una SQL. La escribo estructurada
'en líneas para que apreciemos perfectamente los campos que se crearán
'Para relacionarlo con el código posterior, tened en cuenta que:

27
Visítame en http://bit.ly/NckAccess
'Nombre -> Será el Field(0); Gama -> Será el Field(1); ... ; PrecioAlquiler ->
'Será el Field(6)
miSql = "CREATE TABLE " & nomTablaAux & " (" _
& "Nombre STRING," _
& "Gama STRING," _
& "Terreno STRING," _
& "TipoCombustible STRING," _
& "PrevGtoComb CURRENCY," _
& "Sobreprima CURRENCY," _
& "PrecioAlquiler CURRENCY" _
& ")"
'Creamos la tabla a través de la ejecución de la SQL
dbs.Execute miSql

'Defino los recordset


Set rstIN = dbs.OpenRecordset("TAutos", dbOpenForwardOnly)
Set rstOUT = dbs.OpenRecordset("TAuxInfoCliente", dbOpenTable)
'Aquí el proceso va a ser doble: vamos a recorrer los registros de TAutos. Situados
'en un registro concreto, vamos a crear los elementos de la clase. Una vez creados
'los vamos a traspasar a nuestra tabla auxiliar
With rstIN
Do Until .EOF
'Creo un ejemplar de clase
Set miCoche = New clsAuto
'Relleno los datos 'fijos' con la información del registro
miCoche.nombreCoche = .Fields("NomAuto").Value
miCoche.gamaCoche = .Fields("GamaAuto").Value
miCoche.terrenoCoche = .Fields("TerrenoAuto").Value
miCoche.tipoCombCoche = .Fields("TipoCombAuto").Value
miCoche.consumMedioCoche = .Fields("ConsumMedAuto").Value
'Obtengo el origen de los datos variables buscando en las tablas auxiliares
buscoPrecioComb = DLookup("[ImpComb]", "TPrecioComb", "[TipoComb]='" _
& miCoche.tipoCombCoche & "'")
buscoPrima = DLookup("[Prima]", "TPrimas", "[GamaPrima]='" _
& miCoche.gamaCoche & "'")
buscoPrecioAlquiler = DLookup("[Precio]", "TPrecios", "[GamaPrec]='" _
& miCoche.gamaCoche & "'")
'Paso los datos a la tabla auxiliar
rstOUT.AddNew
rstOUT.Fields(0).Value = miCoche.nombreCoche
rstOUT.Fields(1).Value = miCoche.gamaCoche
rstOUT.Fields(2).Value = miCoche.terrenoCoche
rstOUT.Fields(3).Value = miCoche.tipoCombCoche
rstOUT.Fields(4).Value = miCoche.gasolinaCoche(numKm, buscoPrecioComb)
rstOUT.Fields(5).Value = miCoche.sobreprimaCoche(buscoPrima, diasAlq)
rstOUT.Fields(6).Value = miCoche.alquilerCoche(diasAlq, buscoPrecioAlquiler)
rstOUT.Update
.MoveNext
Loop
End With
Salida:
'Cerramos conexiones y liberamos memoria
rstIN.Close
rstOUT.Close
dbs.Close
Set rstIN = Nothing
Set rstOUT = Nothing
Set dbs = Nothing
Exit Sub
sol_err:
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description & vbCrLf & vbCrLf _
& "El origen del error está en " & Err.Source
Resume Salida
End Sub

Fijaos en cómo se me ha ocurrido articular el proceso: como un obrero en medio de dos cintas
de montaje: por una de las cintas (rstIN) llega un paquete (la tabla TAutos), nuestro obrero (la
clase) le pone los “lacitos” para dejarlo “guapo” y lo deposita sobre la otra cinta (rstOUT) listo

28
Visítame en http://bit.ly/NckAccess
para servir.

Como curiosidad cabe remarcar que en el control de errores


he añadido que el mensaje nos muestre el origen del error
a través de Err.Source, dado que se manejan bastantes
elementos. Ello nos ayudará a depurar el error, si se
produce, con mayor facilidad.

Como podemos ver, nuestro módulo se ha “simplificado”


bastante en cuanto a cálculos porque dichos cálculos ya los
hemos establecido a través de métodos en nuestra clase.
Así, hemos podido realizar las siguientes acciones:

...
'Relleno los datos 'fijos' con la información del registro
...

para dichos datos 'fijos', y con los datos variables hemos necesitado, simplemente recuperar el
valor de los argumentos a través de las variables buscoXXX y pasárselos al método, a través
de las líneas


'Obtengo el origen de los datos variables buscando en las tablas auxiliares
buscoPrecioComb = DLookup("[ImpComb]", "TPrecioComb", "[TipoComb]='" _
& miCoche.tipoCombCoche & "'")
buscoPrima = DLookup("[Prima]", "TPrimas", "[GamaPrima]='" _
& miCoche.gamaCoche & "'")
buscoPrecioAlquiler = DLookup("[Precio]", "TPrecios", "[GamaPrec]='" _
& miCoche.gamaCoche & "'")


rstOUT.Fields(4).Value = miCoche.gasolinaCoche(numKm, buscoPrecioComb)
rstOUT.Fields(5).Value = miCoche.sobreprimaCoche(buscoPrima)
rstOUT.Fields(6).Value = miCoche.alquilerCoche(diasAlq, buscoPrecioAlquiler)

También fijaos en que los datos “externos”, que deben ser proporcionados por nuestro cliente
(previsión de kilómetros a recorrer y días que quiere alquilar el vehículo) y que, lógicamente,
no sabemos a priori, los paso como argumentos del procedimiento, a través de


Public Sub creoTAux(numKm As Long, diasAlq As Integer)

Sigamos: para el proceso que sigue vamos a necesitar que la tabla auxiliar TAuxInfoCliente
esté creada. Para ello creamos, en el mismo módulo de trabajo mdlAuto, un procedimiento que
eliminaremos tras su ejecución (o, si no queremos eliminarlo, podemos convertir en
comentario), y que será el mismo del código anterior con unas ligerísimas modificaciones
(prácticamente es un copy-paste de un fragmento del código anterior; os marco en negrita lo
que se debe cambiar, que está hacia el final del código):

29
Visítame en http://bit.ly/NckAccess
Private Sub creoTablaPorPrimeraVez()
'Creamos la estructura de la tabla a través de una SQL.
miSql = "CREATE TABLE " & nomTablaAux & " (" _
& "Nombre STRING," _
& "Gama STRING," _
& "Terreno STRING," _
& "TipoCombustible STRING," _
& "PrevGtoComb CURRENCY," _
& "Sobreprima CURRENCY," _
& "PrecioAlquiler CURRENCY" _
& ")"
'Creamos la tabla a través de la ejecución de la SQL
CurrentDb.Execute miSql
End Sub

Nos situamos sobre ese Sub y pulsamos F5. Nuestra tabla se creará. Ya podemos borrar el
código, o convertirlo en comentario. Tened en cuenta que:

– Si la tabla ya existe nos dará error de código


– Puede ser que no veamos la tabla creada en Access. Si es así seleccionad cualquier
tabla y pulsad F5 para actualizar. La tabla recién creada nos aparecerá “de la nada”.

Vamos a crearnos un informe sobre la tabla TAuxInfoCliente, y lo llamaremos RAuxInfoCliente.


Eso no tiene mayor complicación ya para nosotros.

Y ya llegamos a la fase en el que el usuario tendrá una participación activa. Vamos a crearnos
un formulario en blanco que llamaremos FInfoCliente. Yo lo crearé con TextBox para simplificar,
pero lógicamente ya sabríamos crear otros controles, como cuadros combinados o cuadros de
lista, para hacerlo más atractivo y eficiente de cara al usuario. Lo que haremos será lo
siguiente:

• Insertar un textBox, que llamaremos txtGama


• Insertar un textBox, que llamaremos txtTerreno
• Insertar un textBox, que llamaremos txtKm
• Insertar un textBox, que llamaremos txtDiasAlq
• Insertar un botón de comando, que llamaremos cmdAbreRAuxInfoCliente

En definitiva, una cosa así:

30
Visítame en http://bit.ly/NckAccess
Por cierto, y por si alguien no lo sabe, para establecer el texto de la ayuda contextual debemos
ir a las propiedades del control → Otras → Texto de Ayuda del control

El código que debemos asignar al botón de comando será el


siguiente (ojo: no pondré ningún control para validar la
introducción de los valores para simplificar el código;
vosotros ya deberíais saber qué líneas de código os
servirían para controlar eso):


Private Sub cmdAbreRAuxInfoCliente_Click()
'Declaramos las variables
Dim vGama As String, vTerreno As String
Dim vKm As Long, vDias As Integer
Dim filtroDeseosCliente As String
'Asignamos valor a las variables
vGama = Me.txtGama.Value
vTerreno = Me.txtTerreno.Value
vKm = Me.txtKm.Value
vDias = Me.txtDiasAlq.Value
'Llamamos al procedimiento creoTAux, asignándole los parámetros requeridos
Call creoTAux(vKm, vDias)
'Creo el filtro para abrir el informe
filtroDeseosCliente = "[Gama]='" & vGama & "' AND [Terreno]='" & vTerreno & "'"
'Abro el informe, filtrándolo por las peticiones del cliente
DoCmd.OpenReport "RAuxInfoCliente", acViewPreview, , filtroDeseosCliente
End Sub

Y creo que ya está. Esta vez no diré aquello de... ¿fácil, verdad? 

PARA FINALIZAR ESTE CAPÍTULO


Las clases también nos permiten definir eventos (usaríamos EVENT), llamar a dichos eventos
(tenemos para ello el RAISE EVENT), y algunas cosillas más. Sin embargo, vamos a pararnos
aquí. Creo que ya tenemos bastante material para programar nuestras BD's.

Alguien me podría objetar: “para desarrollar este último ejemplo se podrían haber utilizado
otros métodos, y quizá no tan complicados”, y yo diré: “Totalmente de acuerdo... si sólo nos
centramos en el ejemplo”. Pero la “gracia” de lo anterior es que nuestra futura aplicación no es
sólo darle la información que hemos comentado al cliente, sino que, hipotéticamente, la BD
nos serviría para gestionar nuestro negocio. Y la pregunta es: “Si, por ejemplo, tenemos que
emitir una factura al cliente, ¿deberíamos volver a calcular todo de nuevo mediante código? O,
si por ejemplo, un histórico nos muestra una media de días que se alquila un coche de una
determinada gama, y una media de kilómetros que realiza dicho automóvil, y queremos sacar
una previsión de cara al año siguiente, ¿debemos volver a realizar por tercera vez todos los
cálculos? ¿No es mejor tener los cálculos y datos “clave” recogidos en nuestra clase, y acceder
a ella cuando los necesitemos? Porque así sólo necesitamos programar dichos cálculos una sola
vez, ¿no es cierto?”

Más alla, podemos exportar la estructura básica de la BD (tablas) y módulos de clase y


estándar, e importarlos en otra BD a la que queramos dar otra utilidad. Con ello todo el trabajo
de programación, en lo que a estos elementos se refiere, ya lo tenemos hecho, y si hay que
adaptarlo no debería suponernos el mismo trabajo que empezar de cero.

En fin... que creo que, según cómo sea nuestro proyecto, la utilización de clases es un recurso

31
Visítame en http://bit.ly/NckAccess
muy útil... y ahora ya sabemos cómo manejarlo.

Espero que lo que hayáis podido aprender de este capítulo os sea útil.

Un saludo y...

¡suerte!

32
Visítame en http://bit.ly/NckAccess

También podría gustarte