|
Curso básico
de programación en Visual Basic
Lección
43
Polimorfismo
mediante interfaces
|
Realizamos
programas para la gestión de empresas. Empresas medianas y
pequeñas. Programas de contabilidad, cartera de pedidos
clientes proveedores, facturación control de albaranes,
tesorería cartera de cobros y pagos y estadísticas.
Nuestro
agradecimiento a todos los que por unas causas o por otras
visitan nuestra web. Gestión de empresas PYMES. Curso de
programación de Visual Basic.
|
Ha pasado algo más
de un mes desde la entrega anterior,
pero al menos estoy en lo que me propuse: publicar al menos una
entrega cada mes, para que no te aburras y así también evitaré
que me eches la bronca por olvidarme de actualizar el Curso Básico
de Visual Basic... sí, que algunos me regañan y todo... snif!
En esta entrega
vamos a continuar con las clases, aunque esta entrega tratará algo
que puede que te vuelva un poco más "turuleta" de lo que
ya puedas estar... e incluso puede que genere más tráfico de
correo a mi cuenta del que a mi me gustaría, ya que puede ser que
no te enteres de nada... no, no te estoy llamando ignorante... ¡ni
se me ocurriría! pero es que de lo que aquí vamos a tratar es algo
que puede que al principio te confunda y acabes por pensar que...
¡no me entero de nada! ¡abandono! y no es eso lo que me gustaría,
no quiero que abandones... sólo piensa que si el Guille se ha
enterado... ¡cualquiera se puede enterar! y no lo digo por decir,
que a pesar de lo que muchos se creen soy más torpe que... en
fin... no se con que "bicho torpe" compararme, pero seguro
que alguno existirá... je, je. En serio... si no te enteras, no
desesperes... que tarde o temprano acabarás por enterarte... ¡te
lo aseguro!
Un repaso para ir entrando en
calor...
Vamos a repasar lo
que hasta ahora hemos visto, no te asustes, no vamos a repasar el
contenido de las 42 entregas anteriores, sólo lo relacionado con
las clases y la Programación Orientada a Objetos (o lo que más se
le parezca, ya que el Visual Basic no es realmente un lenguaje
orientado a objetos, entre otras cosas porque no soporta la
herencia, cosa que si hace su primo Visual Basic .NET)
Visual Basic no es
un lenguaje orientado a objetos, eso ya lo sabemos, (y
seguro que algunos no los habrán recordado en más de una ocasión),
pero hay que entender un poco de ese tema para sacarle el máximo
rendimiento a esto de las clases en Visual Basic, así que, de forma
resumida, vamos a ver que características de la OOP (Object
Oriented Programming, es que prefiero usar las siglas esas en
lugar de POO, ya que parece que estoy hablando de un río de Italia
que suele salir en los crucigramas...) que nuestro querido
Visual Basic soporta:
Encapsulación:
Esta característica de la
OOP es la facultad de unificar el código y los datos que la clase u
objeto contiene, así como ocultar el código que maneja dicha
información. La encapsulación nos ayuda a olvidarnos de cual es la
implementación realizada en los procedimientos (métodos y
propiedades) de una clase, para que sólo nos preocupemos de cómo
usarlos.
Polimorfismo:
Polimorfismo viene del griego y significaría algo así como:
muchas-formas. Todos los métodos implementados en las clases deben
tener una forma de comportarse, así como ser consistente con la
información que debe tratar, ya que, como te he comentado antes, la
encapsulación es la característica que permite ocultar cómo
están implementados (o codificados) los métodos y propiedades de
las clases y el polimorfismo sería el contrato firmado para que
esos procedimientos se utilicen de forma adecuada.
Bueno, no es realmente eso, pero a ver si así lo entiendes, ya que,
creo que esto no se entiende hasta que se "demuestra", a
ver si con la siguiente "definición" queda algo más
claro:
Se dice que una clase es polimórfica cuando podemos usar sus
métodos y propiedades sin importarnos qué objeto los implementa.
Ya que Polimorfismo significa eso: múltiples formas, es decir, si
un objeto implementa el método Show, podemos usar ese método
siempre de la misma forma sin importarnos el objeto que estemos
usando, si esto no es así, no estaremos utilizando de forma
correcta el Polimorfismo.
El Polimorfismo en Visual Basic se puede usar de dos formas
diferentes, según se compruebe si el procedimiento (o miembro de la
clase) pertenece al objeto que lo utiliza, en tiempo de diseño o en
tiempo de ejecución, esto también es conocido como compilación
temprana (early binding) o compilación tardía (late
binding) respectivamente.
Además de estas
dos características, aclaremos los siguientes puntos:
Métodos:
Los métodos son las acciones que una clase puede ejecutar,
normalmente son procedimientos de tipo Sub o Function.
Para que sepamos cuando un procedimiento es un método, debemos
pensar si dicho procedimiento realizará una acción sobre los datos
que la clase maneja. Esta aclaración es porque en algunos sitios te
dirán que las funciones pueden ser propiedades, pero una función
siempre es un método, aunque en ocasiones estén implementadas de
forma que puedan hacernos pensar lo contrario.
Propiedades:
Las propiedades de una clase (u objeto) son las características de
esa clase.
Las propiedades podrían ser también las características de los
datos que la clase maneja (o manipula).
como deberías saber, las propiedades las podemos declarar de dos
formas: creando un procedimiento de tipo Property o bien
declarando una variable pública.
¿No te queda
claro?
Por ejemplo, si nuestra clase "maneja" a nuestros colegas,
(realmente maneja o trata la información de
nuestros colegas, no a nuestros colegas... aunque algunas veces
sería interesante que pudiésemos manejar a un colega y
"encapsularlo" dentro de una clase, para que nos deje
tranquilos... je, je), las propiedades serían los datos o
características que tenemos de nuestros colegas, por ejemplo: el
nombre, el correo electrónico, la edad, si está loco... por la
música, por ejemplo, etcétera...
Mientras que los métodos podrían ser las acciones que queremos
realizar sobre esos datos: que los muestre, que los borre o que los
cambie, incluso podríamos hacer que esa información se almacenara
en un archivo o en una base de datos, incluso enviarles de forma
automática un mensaje cuando sea su cumpleaños, etc.
Ahora empecemos con
los quebraderos de cabeza.
¿Cómo podemos usar la
característica de la Encapsulación?
Aquí no tenemos
que hacer nada especial, el mero hecho de crear un método o una
propiedad en una clase ya implica que estamos usando la
característica de la encapsulación.
¿Cómo podemos usar la característica
del Polimorfismo?
Como te comenté
antes, hay dos formas de implementar (usar) el polimorfismo, según
se haga en tiempo de diseño o en tiempo de ejecución.
Empecemos viendo cómo hacerlo de la forma "menos
recomendada", aunque algunas veces será la única, pero te
recomiendo que la evites siempre que puedas.
Si declaras una
variable de tipo Object, (e incluso uno de tipo Variant),
podrás asignarle cualquier objeto a dicha variable y acceder a los
miembros del objeto, veamos un ejemplo:
Si tenemos un objeto del tipo cColega (el código lo vimos en
la entrega 37 y se amplió en la
entrega 39), podríamos tener algo como esto:
Private Sub cmdLate_Click()
' declaramos las variables
Dim unColega As cColega
' también se puede declarar como As Variant
Dim o As Object
'
' creamos el objeto de tipo colega
Set unColega = New cColega
' y le añadimos algunos datos
unColega.Nombre = "Guille"
unColega.email = "guille@costasol.net"
'
' asignamos el colega (unColega) al objeto (o)
Set o = unColega
' mostramos el nombre del colega
MsgBox "El colega es: " & o.Nombre
'
End Sub
Esto funcionaría
como se espera: mostraría el nombre correcto.
Esto es Polimorfismo, aunque se use compilación en tiempo tardío
(late-binding), es decir, el runtime del Visual Basic no sabe nada
de la propiedad Nombre
del objeto referenciado por la variable o,
pero intenta acceder a dicha propiedad y como "sabemos"
que la tiene, se muestra el nombre que hemos asignado.
En caso de que asignemos a esa variable un objeto que no
tenga la propiedad Nombre, el código se compilará
correctamente, y no será hasta que esté ejecutándose cuando el VB
se de cuenta de que el objeto referenciado por la variable o
no tiene una propiedad llamada Nombre, con lo que se
producirá un error en tiempo de ejecución.
Por ejemplo, si en
lugar de acceder a la propiedad Nombre (que si la tiene),
queremos mostrar los apellidos mediante este código:
MsgBox "El colega es:
" & o.Apellidos
Como resulta que el objeto referenciado por la variable o
(que es del tipo cColega), no tiene una propiedad llamada Apellidos,
el runtime de Visual Basic nos mostrará este mensaje cuando llegue
a esa línea de código:

Error en tiempo de ejecución al acceder a una propiedad que no
existe
Pero hasta que no
llegue a esa línea no sabremos que había algo mal.
Por supuesto, podemos usar detección de errores para que ese
mensaje no se muestre, pero eso sólo hará que no se muestre, no
que no se produzca, que es al fin y al cabo lo que debemos evitar.
Para poder
solventar este tipo de problemas, podemos hacer dos cosas:
1- La que te he dicho antes, utilizar detección de errores.
2- Usar un objeto del tipo específico, así siempre sabremos si
dicho objeto tiene la propiedad a la que queremos acceder y si no la
tiene, el VB nos avisará en tiempo de diseño, antes de que se
compile el programa.
Pero, (¿recuerdas
que siempre hay un pero?), ¿qué hacemos si queremos
crear un procedimiento que reciba un parámetro de tipo genérico y
lo mismo nos sirva para un objeto del tipo cColega como otro
de otro tipo distinto?, por ejemplo, si hemos creado una clase
llamada cColega2 y que también tiene la propiedad Nombre
y otras de las incluidas en cColega, además de algunas
nuevas.
Pues nada... ya que esto en VB no se puede hacer de forma
"limpia"... o si lo prefieres de forma natural o
coherente...
Lo único que podríamos hacer es usar como parámetro de ese
procedimiento una variable de tipo Object, que acepta
cualquier tipo de objeto, (incluso formularios y controles), y hacer
una serie de comprobaciones, para saber si dicho objeto es de un
tipo u otro y usar una variable adecuada a dicho tipo.
Veamos el código de la clase cColega2 (versión
simplificada) y el de ese procedimiento:
La clase cColega2:
'---------------------------------------------------------------------------
' cColega2
' Clase ampliada del tipo Colega
'
' ©Guillermo 'guille' Som, 2002
'---------------------------------------------------------------------------
Option Explicit
Public Nombre As String
Public Apellidos As String
'
Public email As String
Public FechaNacimiento As Date
El procedimiento, (que
estará, por ejemplo en un formulario):
Private Sub mostrar(elObjeto As Object)
If TypeOf elObjeto Is cColega Then
' si el objeto es de tipo cColega
' mostrar sólo el nombre
MsgBox "El colega es: " & elObjeto.Nombre
ElseIf TypeOf elObjeto Is cColega2 Then
' si el objeto es de tipo cColega2
' mostrar el nombre y apellidos
MsgBox "El colega es: " & elObjeto.Nombre & " " & elObjeto.Apllidos
End If
End Sub
Lo que aquí
hacemos es: recibir un objeto de cualquier tipo como parámetro,
después comprobamos si es del tipo cColega y mostramos el
nombre, si es del tipo cColega2, mostramos el nombre y los
apellidos.
Pero este código,
para mi gusto no sería la mejor solución, todavía podemos
equivocarnos al teclear cualquiera de las propiedades y no nos
daremos cuenta hasta que estemos ejecutando el programa.
Si eres una persona observadora, te habrás fijado que en lugar de
escribir Apellidos, he escrito Apllidos y esa no es
una propiedad de la clase cColega2.
Para solucionar ese
inconveniente podemos hacer algo como esto:
Private Sub mostrar2(elObjeto As Object)
If TypeOf elObjeto Is cColega Then
Dim colega1 As cColega
Set colega1 = elObjeto
' si el objeto es de tipo cColega
' mostrar sólo el nombre
MsgBox "El colega es: " & colega1.Nombre
ElseIf TypeOf elObjeto Is cColega2 Then
Dim colega2 As cColega2
Set colega2 = elObjeto
' si el objeto es de tipo cColega2
' mostrar el nombre y apellidos
MsgBox "El colega es: " & colega2.Nombre & " " & colega2.Apellidos
End If
End Sub
Con esto, aunque
tengamos que escribir más código, evitaremos usar propiedades no
existentes, ya que será el propio IDE de Visual Basic el que nos
muestre las propiedades y métodos de cada objeto, tal como podemos
comprobar en las siguientes imágenes:

Los miembros de cColega

Los miembros de cColega2
Bien, dirás...
esto está muy bien, pero... ¿esto es polimorfismo?
Pues... si y no... realmente no, aunque podría ser...
La verdad es que no. Al menos si
entendemos polimorfismo como una de las características de la
programación orientada a objetos.
Pero ni el polimorfismo nos solucionaría el problema suscitado por
el uso de dos clases (u objetos) diferentes en el método mostrar
o mostrar2.
Otra cosa sería
que tuviésemos un método Mostrar en cada una de las clases
y en el procedimiento mostrar del formulario se usara dicho
método para que se muestre lo que se tenga que mostrar, en el caso
de cColega sólo se mostraría el Nombre y en el caso
de cColega2 se mostraría el Nombre y Apellidos,
veamos cómo declarar esos métodos en cada una de las clases y
cómo se usaría:
El método Mostrar de la clase cColega:
Public Sub Mostrar()
MsgBox "El nombre es: " & Nombre
End Sub
El método Mostrar de la clase cColega2:
Public Sub Mostrar()
MsgBox "El nombre es: " & Nombre & " " & Apellidos
End Sub
El método mostrar3 del formulario de
prueba:
Private Sub mostrar3(elObjeto As Object)
If TypeOf elObjeto Is cColega Then
Dim colega1 As cColega
Set colega1 = elObjeto
' llamar al método Mostrar del objeto
colega1.Mostrar
ElseIf TypeOf elObjeto Is cColega2 Then
Dim colega2 As cColega2
Set colega2 = elObjeto
' llamar al método Mostrar del objeto
colega2.Mostrar
End If
End Sub
La llamada al método mostrar3:
Private Sub cmdMostrar3_Click()
' declaramos las variables
Dim unColega As cColega
Dim o As Object
'
' creamos el objeto de tipo colega
Set unColega = New cColega
' y le añadimos algunos datos
unColega.Nombre = "Guille"
unColega.email = "guille@costasol.net"
'
' asignamos el colega (unColega) al objeto (o)
Set o = unColega
' lo mostramos
mostrar3 o
'
' creamos un objeto del tipo cColega2...
Dim unColega2 As cColega2
Set unColega2 = New cColega2
' y le asignamos algunos datos
unColega2.Nombre = "Guille"
unColega2.Apellidos = "Som Cerezo"
unColega2.email = "guille@costasol.net"
' lo mostramos
mostrar3 unColega2
End Sub
Aquí tenemos que
observar que el código de Mostrar de las dos clases es muy
parecido, pero cada uno muestra los datos que el que escribió la
clase quiera... a nosotros no debe importarnos, lo único que nos
interesa es que llamando al método Mostrar se mostrarán los
datos (esto es encapsulación).
Por otro lado, en el método mostrar3 usamos dichos métodos
para que se muestre el mensaje que corresponda (esto podría ser
polimorfismo, pero aún no lo es).
Para que realmente
pudiéramos aprovecharnos de las características del polimorfismo,
deberíamos poder crear, (en el formulario), un método mostrar que
nos permitiera llamar al método Mostrar del objeto pasado
como parámetro sin preocuparnos de si el objeto es del tipo cColega
o cColega2.
Esto lo podemos
solucionar mediante las interfaces.
Una interfaz es una especie de plantilla que podemos aplicar a
nuestras clases.
En esa plantilla se definen los métodos y propiedades, así como la
forma en que deben implementarlas las clases que quieran firmar un
contrato con esa interfaz.
Las clases que usen esa interfaz tendrán una definición de dichas
propiedades y métodos tal y como lo indica la plantilla.
¿Y para que sirve
todo eso?
Para que, si una clase implementa dicha interfaz, podamos llamar a
los miembros declarados en ella de forma genérica, sin necesidad de
usar un objeto intermedio o genérico.
Por ejemplo, supongamos que tenemos una interfaz llamada IColega,
si dicha interfaz (o interface si lo prefieres), tiene un método Mostrar
y tanto la clase cColega como cColega2 la implementan,
podríamos mostrar los datos del colega usando el siguiente método
del formulario:
Private Sub mostrar4(elObjeto As Object)
If TypeOf elObjeto Is IColega Then
Dim elColega As IColega
Set elColega = elObjeto
elColega.Mostrar
Else
MsgBox "El objeto no es del tipo IColega"
End If
End Sub
Aquí comprobamos
si el objeto es del tipo IColega, si es así, se llama al
método Mostrar, después de hacer una asignación del objeto
pasado como parámetro a una variable del tipo IColega, en
caso de que no sea de ese tipo, se mostrará un mensaje de aviso.
La declaración de
la interfaz (realmente es una clase) IColega sería algo como
esto:
'---------------------------------------------------------------------------
' IColega
' Interfaz para las clases de tipo Colega
'
' ©Guillermo 'guille' Som, 2002
'---------------------------------------------------------------------------
Option Explicit
Public Sub Mostrar()
' no es necesario escribir código
' sólo es necesario definirlo
End Sub
Como podemos
comprobar, la declaración de una interfaz no tiene porqué contener
código, sólo es necesario indicar cómo se deben declarar los
miembros que dicha clase contenga.
Si tanto la clase cColega
como cColega2 han firmado un contrato para utilizar el
método Mostrar tal y como lo indica la interfaz IColega,
es decir, si esas dos clases "implementan" dicha interfaz,
podremos usar el siguiente procedimiento para llamar al
procedimiento mostrar4 del formulario:
Private Sub cmdMostrar4_Click()
' declaramos las variables
Dim unColega As cColega
Dim o As Object
'
' creamos el objeto de tipo colega
Set unColega = New cColega
' y le añadimos algunos datos
unColega.Nombre = "Guille"
unColega.email = "guille@costasol.net"
'
' asignamos el colega (unColega) al objeto (o)
Set o = unColega
' lo mostramos
mostrar4 o
'
' creamos un objeto del tipo cColega2...
Dim unColega2 As cColega2
Set unColega2 = New cColega2
' y le asignamos algunos datos
unColega2.Nombre = "Guille"
unColega2.Apellidos = "Som Cerezo"
unColega2.email = "guille@costasol.net"
' lo mostramos
mostrar4 unColega2
'
' incluso podríamos llamar al método con otro objeto
mostrar4 Command1
End Sub
En este caso, el
código es casi como el del tercer ejemplo (cmdMostrar3),
sólo que en lugar de llamar a mostrar3, se llama a mostrar4.
También hemos añadido una nueva llamada al método mostrar4,
pero pasándole como parámetro cualquier otro objeto, en este caso
hemos pasado un parámetro del tipo Command, que por supuesto
no implementa IColega, con lo cual se mostrará un aviso
indicándonos que no implementa esa interfaz.
Seguramente la
pregunta que te harás es:
¿Puedo usar ese código directamente y se usará el método Mostrar
de esas dos clases?
La respuesta es:
No.
A pesar de que tanto la clase cColega como cColega2
tengan un método llamado Mostrar no significa que hayan
firmado un contrato con la interfaz IColega.
Por tanto, no podríamos usar ese código, al menos tal y como
están declaradas esas dos clases.
Para poder usar ese código, las dos clases deberían firmar un
contrato con IColega.
Entonces, ¿cómo firmo
ese contrato para las clases cColega y cColega2?
Utilizando la instrucción Implements.
Cuando usamos la
instrucción Implements, la cual hay que usarla en la parte
de las declaraciones de la clase que queremos que la implemente,
seguida del nombre de la interfaz a implementar, se crea un nuevo
objeto en dicha clase, al cual podemos acceder, desde el IDE de VB,
si pulsamos en la lista desplegable de la izquierda, tal como
hacemos, por ejemplo, cuando queremos acceder al código de un
evento de un formulario, tal como podemos ver en la siguiente
captura:

Para que se pueda
mostrar ese nuevo objeto, tendremos que añadir la siguiente línea
a ambas clases:
Implements IColega
Después de hacer
esto, tendremos el objeto IColega en esa lista desplegable y
al seleccionarla, en la derecha nos mostrará los métodos que dicha
interfaz contiene, en este caso sólo tendrá uno: Mostrar.
Y si lo seleccionamos se mostrará el siguiente código:
Private Sub IColega_Mostrar()
End Sub
Dentro tendremos
que escribir el código que se usará cuando se acceda a esta clase
mediante un objeto del tipo IColega.
Pero como lo que queremos es que se use el código que ya hemos
escrito, simplemente llamamos al método Mostrar escrito en
nuestra clase, con lo cual, el código completo quedaría así:
Private Sub IColega_Mostrar()
Me.Mostrar
End Sub
Si la clase IColega
tuviera más de un método, tendríamos que escribir código en
todos los procedimientos que dicha clase contenga, ya que si
implementamos una interfaz, debemos implementar todos
los métodos y propiedades de dicha interfaz.
Implementar
una interfaz es un compromiso, en el que nos comprometemos a hacer
las cosas tal y como dicha interfaz requiere.
Te voy a mostrar el
código completo de la clase cColega2 para que no te quede
duda.
'---------------------------------------------------------------------------
' cColega2
' Clase ampliada del tipo Colega
'
' ©Guillermo 'guille' Som, 2002
'---------------------------------------------------------------------------
Option Explicit
Implements IColega
Public Nombre As String
Public Apellidos As String
'
Public email As String
Public FechaNacimiento As Date
Public Sub Mostrar()
MsgBox "El nombre es: " & Nombre & " " & Apellidos
End Sub
Private Sub IColega_Mostrar()
Me.Mostrar
End Sub
Fíjate que el
método IColega_Mostrar está declarado como Private,
pero eso no debe preocuparte.
Si hacemos lo mismo con la clase cColega,
podremos usar el código mostrado anteriormente sin ningún tipo de
problema.
Para finalizar,
aclararte que cuando usamos lo siguiente:
Dim elColega As IColega
Set elColega = elObjeto
elColega.Mostrar
A la variable elColega,
(que es un objeto del tipo IColega), realmente le estamos
asignando sólo una parte del código de la clase representada por elObjeto,
la parte que implementa dicha interfaz.
Es decir, si la variable elObjeto fuese del tipo cColega,
no estaríamos asignando todo el objeto, sólo una parte del mismo.
Esto podemos comprobarlo si queremos acceder a la propiedad Nombre:
s = elColega.Nombre
Esto daría error, ya que el objeto elColega, (que como
sabemos es del tipo IColega), no tiene una propiedad llamada Nombre.
Sólo me queda
decirte que en Visual Basic todas las clases se pueden
usar con la instrucción Implements. Es decir, no tenemos
porqué crear una clase específica para crear una interfaz.
Pero te recomiendo que lo hagas así, para que sea más evidente y
sobre todo porque así es como tendrás que hacerlo cuando
"migres" a la versión .NET de Visual Basic.
Ahora sí, hasta
aquí hemos llegado en esta ocasión.
En la próxima entrega seguiremos viendo más cosas relacionadas con
las clases y, casi con seguridad, un ejemplo más práctico sobre la
ventaja de usar esto de la implementación de interfaces.
Pero eso será...
casi con seguridad, el año que viene, mientras tanto... espero que
pases unas Felices Fiestas... sí, aunque no seas creyente... que no
hay que ser creyente para, aunque sea una vez al año, desear cosas
buenas a la gente... je, je.
Nos vemos
Guillermo
Aquí
tienes el fichero zip con el código usado en esta entrega:
basico43_cod.zip 5.02 KB

|