Curso Básico de Programación
en Visual Basic

 

Entrega Cuarenta y tres: 21/Dic/2002
por Guillermo "guille" Som

Si quieres linkar con las otras entregas, desde el índice lo puedes hacer

 

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 = "mensaje@elguille.info"
    '
    ' 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                                                          (21/Dic/02)
' 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 = "mensaje@elguille.info"
    '
    ' 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 = "mensaje@elguille.info"
    ' 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                                                           (21/Dic/02)
' 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 = "mensaje@elguille.info"
    '
    ' 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 = "mensaje@elguille.info"
    ' 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                                                          (21/Dic/02)
' 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


 
entrega anterior ir al índice siguiente entrega

Ir al índice principal del Guille