índice del curso de VB .NET

Curso de iniciación a la programación
con Visual Basic .NET

Entrega número once, (18/Feb/2003)
Publicada el 18/Feb/2003


Bueno, aún no ha pasado un mes completo desde la entrega anterior, así que... no deberías quejarte demasiado, ya que me estoy comportando como un tío serio y formal... ¡pero no te lo vayas a creer! que eso sólo son las apariencias... je, je...

Como te comenté en la entrega anterior, en esta ocasión vamos a ver un tema del que acabarás hasta la coronilla, entre otras cosas porque es algo que "tienes" que saber y si lo dominas, cosa que pretendo y espero conseguir, te será mucho más fácil hacer cualquier tipo de programa o utilidad con Visual Basic .NET o con cualquier otro lenguaje de la familia .NET, como puede ser C#.
Lo que si te pido es paciencia y mucha mente "en blanco", es decir, déjate llevar y no desesperes si de algo no te enteras, ya que intentaré que todo esto que empezaremos en esta entrega llegue a formar parte de ti... no, no te voy a implantar ningún chip, así que no te asustes... pero este tema es algo de lo que los programadores de Visual Basic debemos estar "orgullosos" (por decir algo) de que al fin... se nos permita tener, de forma más o menos completa, ya que hasta el momento sólo teníamos acceso a ello de forma limitada... Bueno, menos rollo y vamos al tema, que es ni más ni menos que:

La programación orientada a objetos

Todo .NET Framework está basado en clases (u objetos). A diferencia de las versiones anteriores de Visual Basic, la versión .NET de este lenguaje basa su funcionamiento casi exclusivamente en las clases contenidas en .NET Framework, además casi sin ningún tipo de limitaciones. Debido a esta dependencia en las clases del .NET Framework y sobre todo a la forma "hereditaria" de usarlas, Visual Basic .NET tenía que ofrecer esta característica sin ningún tipo de restricciones. Y, por suerte para nosotros, esto es así... por tanto, creo conveniente que veamos de que va todo esto de la Programación Orientada a Objetos.

Nota:
Seguramente, en esta y en las próximas entregas, utilice las siglas OOP cuando me refiera a la programación orientada a objetos, esto es así porque éstas son las siglas de Object Oriented Programming, que es como se dice en inglés y como estoy acostumbrado a usar OOP, pues...

Aunque no soy muy dado a esto de las cosas teóricas, vamos a ver de que va todo esto de la programación orientada a objetos y cuales son las "condiciones" que cualquier lenguaje que se precie de serlo debe cumplir.

Los tres pilares de la Programación Orientada a Objetos

Según se dice por ahí, cualquier lenguaje basado en objetos debe cumplir estos tres requisitos:

  1. Herencia
  2. Encapsulación
  3. Polimorfismo

Nota:
Algunos autores añaden un cuarto requisito: la abstracción, pero este último está estrechamente ligado con la encapsulación, ya que, de echo, es prácticamente lo mismo, cuando te lo explique, lo comprobarás y podrás decidir por tu cuenta si son tres o cuatro los pilares de la OOP...

Veamos que es lo que significa cada uno de ellos:

Herencia

Esto es lo que la documentación de Visual Studio .NET nos dice de la herencia:

Una relación de herencia es una relación en la que un tipo (el tipo derivado) se deriva de otro (el tipo base), de tal forma que el espacio de declaración del tipo derivado contiene implícitamente todos los miembros de tipo no constructor del tipo base.
(ms-help://MS.VSCC.2003/MS.MSDNVS.3082/vbls7/html/vblrfVBSpec4_2.htm)

Está claro ¿verdad?
¿Cómo? ¿Que no te has enterado? A ver, a ver... y si probamos con esto otro:

La herencia es la capacidad de una clase de obtener la interfaz y comportamiento de una clase existente.

Ya nos vamos entendiendo.

Para que te quede un poco más claro, pero no esperes milagros, aquí tienes otra definición de herencia:

La herencia es la cualidad de crear clases que estén basadas en otras clases. La nueva clase heredará todas las propiedades y métodos de la clase de la que está derivada, además de poder modificar el comportamiento de los procedimientos que ha heredado, así como añadir otros nuevos.

Resumiendo: Gracias a la herencia podemos ampliar cualquier clase existente, además de aprovecharnos de todo lo que esa clase haga... (sí, ya se que aún no hemos profundizado en lo que son las clases, pero... es que esto no es fácil de explicar, al menos para que se entienda a la primera, así que muuucha paciencia...)

Para que lo entiendas mejor, veamos un ejemplo clásico:
Supongamos que tenemos una clase Gato que está derivada de la clase Animal.
El Gato hereda de Animal todas las características comunes a los animales, además de añadirle algunas características particulares a su condición felina.
Podemos decir que un Gato es un Animal, lo mismo que un Perro es un Animal, ambos están derivados (han heredado) de la clase Animal, pero cada uno de ellos es diferente, aunque en el fondo los dos son animales.
Esto es herencia: usar una clase base (Animal) y poder ampliarla sin perder nada de lo heredado, pudiendo ampliar la clase de la que se ha derivado (o heredado).

¿Qué? Sigues sin enterarte, ¿verdad?
Bueno, que le vamos a hacer... pero no desanimes y sigue leyendo... que si bien esto no es cosa fácil, tampoco es algo que sea imposible de entender, (hasta el Guille se ha enterado de que va todo esto de la herencia, así que... paciencia).

 

Encapsulación

A ver si con la encapsulación tenemos más suerte:

La encapsulación es la capacidad de contener y controlar el acceso a un grupo de elementos asociados. Las clases proporcionan una de las formas más comunes de encapsular elementos.
(ms-help://MS.VSCC.2003/MS.MSDNVS.3082/vbcn7/html/vbconClassModulesPuttingDataTypesProceduresTogether.htm)

Está crudo ¿verdad?

La encapsulación es la capacidad de separar la implementación de la interfaz de una clase del código que hace posible esa implementación. Esto realmente sería una especie de abstracción, ya que no nos importa cómo esté codificado el funcionamiento de una clase, lo único que nos debe interesar es cómo funciona...

Para que nos vayamos entendiendo, cuando digo: la implementación de la interfaz de una clase, me refiero a los miembros de esa clase: métodos, propiedades, eventos, etc. Es decir, lo que la clase es capaz de hacer.

Cuando usamos las clases, éstas tienen una serie de características (los datos que manipula) así como una serie de comportamientos (las acciones a realizar con esos datos). Pues la encapsulación es esa capacidad de la clase de ocultarnos sus interioridades para que sólo veamos lo que tenemos que ver, sin tener que preocuparnos de cómo está codificada para que haga lo que hace... simplemente nos debe importar que lo hace.

Si tomamos el ejemplo de la clase Gato, sabemos que araña, come, se mueve, etc., pero el cómo lo hace no es algo que deba preocuparnos, salvo que se lance sobre nosotros... aunque, en ese caso, lo que deberíamos tener es una clase "espanta-gatos" para quitárnoslo de encima lo antes posible...

 

Polimorfismo

Pues no sé que decirte... si en los dos casos anteriores la cosa estaba complicada... En fin... a ver que es lo que nos dice la documentación de VS.NET sobre el polimorfismo:

El polimorfismo se refiere a la posibilidad de definir múltiples clases con funcionalidad diferente, pero con métodos o propiedades denominados de forma idéntica, que pueden utilizarse de manera intercambiable mediante código cliente en tiempo de ejecución.
(ms-help://MS.VSCC.2003/MS.MSDNVS.3082/vbcn7/html/vbconInheritancePolymorphismAllThat.htm)

Mira tu por dónde, esta definición si que me ha gustado...

Vale, que a ti te ha dejado igual que hace un rato... pues... no se yo que decirte... es que, si no lo entiendes, la verdad es que podrías dedicarte a otra cosa... je, je, tranqui colega, que ya te he comentado antes que había que tener la mente en blanco... y lo que no te dije es que seguramente la ibas a seguir teniendo en blanco...

Ya en serio, el Polimorfismo nos permite usar miembros de distintas clases de forma genérica sin tener que preocuparnos si pertenece a una clase o a otra.
Siguiendo con el ejemplo de los animales, si el Gato y el Perro pueden morder podríamos tener un "animal" que muerda sin importarnos que sea el Gato o el Perro, simplemente podríamos usar el método Morder ya que ambos animales tienen esa característica "animal mordedor".

Seguramente estarás pensando: Guille, te podrías haber ahorrado toda estas parrafadas, ya que me he quedado igual o peor que antes... y estarás en lo cierto... pero... ya te comenté que esto de la teoría no es lo mío, así que... cuando lo veamos con ejemplos "reales" seguro que lo entenderás mejor. Así que, como te dije antes: paciencia... mucha paciencia...

 

Realmente todo este rollo sobre las características de la Programación Orientada a Objetos es para que sepas que ahora con Visual Basic es posible hacer todo esto; cómo hacerlo, será lo que realmente tenga yo que enseñarte, así que... sigue leyendo que aún no hemos terminado.

Y para que podamos ver ejemplos antes debemos tener claro una serie de conceptos, como por ejemplo:

Las clases

Ya hemos visto antes lo que son las clases. Ya que todo lo que tiene el .NET Framework, en realidad son clases.

Una clase no es ni más ni menos que código. Aunque dicho de esta forma, cualquier programa sería una clase.

Cuando definimos una clase, realmente estamos definiendo dos cosas diferentes: los datos que dicha clase puede manipular o contener y la forma de acceder a esos datos.

Por ejemplo, si tenemos una clase de tipo Cliente, por un lado tendremos los datos de dicho cliente y por otro la forma de acceder o modificar esos datos. En el primer caso, los datos del Cliente, como por ejemplo el nombre, domicilio etc., estarán representados por una serie de campos o propiedades, mientras que la forma de modificar o acceder a esa información del Cliente se hará por medio de métodos.
Esas propiedades o características y las acciones a realizar son las que definen a una clase.

 

Los Objetos

Por un lado tenemos una clase que es la que define un "algo" con lo que podemos trabajar. Pero para que ese "algo" no sea un "nada", tendremos que poder convertirlo en "algo tangible", es decir, tendremos que tener la posibilidad de que exista. Aquí es cuando entran en juego los objetos, ya que un objeto es una clase que tiene información real.

Digamos que la clase es la "plantilla" a partir de la cual podemos crear un objeto en la memoria.
Por ejemplo, podemos tener varios objetos del tipo Cliente, uno por cada cliente que tengamos en nuestra cartera de clientes, pero la clase sólo será una.
Dicho de otra forma: podemos tener varias instancias en memoria de una clase. Una instancia es un objeto (los datos) creado a partir de una clase (la plantilla o el código).

 

Para entender mejor todo este galimatías, desglosemos las clases:

Los miembros de una clase

Las clases contienen datos, esos datos suelen estar contenidos en variables. A esas variables cuando pertenecen a una clase, se les llama: campos o propiedades.

Por ejemplo, el nombre de un cliente sería una propiedad de la clase Cliente. Ese nombre lo almacenaremos en una variable de tipo String, de dicha variable podemos decir que es el "campo" de la clase que representa al nombre del cliente.

Por otro lado, si queremos mostrar el contenido de los campos que contiene la clase Cliente, usaremos un procedimiento que nos permita mostrarlos, ese procedimiento será un método de la clase Cliente.

Por tanto, los miembros de una clase son las propiedades (los datos) y los métodos las acciones a realizar con esos datos.

Como te he comentado antes, el código que internamente usemos para almacenar esos datos o para, por ejemplo, mostrarlos, es algo que no debe preocuparnos mucho, simplemente sabemos que podemos almacenar esa información (en las propiedades de la clase) y que tenemos formas de acceder a ella, (mediante los métodos de dicha clase), eso es "abstracción" o encapsulación.

 

Seguro que dirás: Vale, todo esto está muy bien, pero... ¿cómo hago para que todo eso sea posible?
¿Cómo creo o defino una clase? y ya que estamos, ¿cómo defino las propiedades y los métodos de una clase?

Aquí quería yo llegar... y te lo explico ahora mismo.

Crear o definir una clase

Al igual que existen instrucciones para declarar o definir una variable o cualquier otro elemento de un programa de Visual Basic, existen instrucciones que nos permiten crear o definir una clase.

Para crear una clase debemos usar la instrucción Class seguida del nombre que tendrá dicha clase, por ejemplo:

Class Cliente

A continuación escribiremos el código que necesitemos para implementar las propiedades y métodos de esa clase, pero para que Visual Basic sepa que ya hemos terminado de definir la clase, usaremos una instrucción de cierre:

End Class

Por tanto, todo lo que esté entre Class <nombre> y End Class será la definición de dicha clase.

Definir los miembros de una clase

Para definir los miembros de una clase, escribiremos dentro del "bloque" de definición de la clase, las declaraciones y procedimientos que creamos convenientes. Veamos un ejemplo:

Class Cliente

    Public Nombre As String

    Sub Mostrar()
        Console.WriteLine("El nombre del cliente: {0}", Nombre)
    End Sub

End Class

En este caso, la línea Public Nombre As String, estaría definiendo una propiedad o "campo" público de la clase Cliente.

Por otro lado, el procedimiento Mostrar sería un método de dicha clase, en esta caso, nos permitiría mostrar la información contenida en la clase Cliente.

Esta es la forma más simple de definir una clase. Y normalmente lo haremos siempre así, por tanto podemos comprobar que es muy fácil definir una clase, así como los miembros de dicha clase.

Pero no sólo de clases vive el Visual Basic... o lo que es lo mismo, ¿para que nos sirve una clase si no sabemos crear un objeto basado en esa clase...? Así que, sepamos cómo crearlos.

Crear un objeto a partir de una clase

Como te he comentado antes, las clases definen las características y la forma de acceder a los datos que contendrá, pero sólo eso: los define.
Para que podamos asignar información a una clase y poder usar los métodos de la misma, tenemos que crear un objeto basado en esa clase, o lo que es lo mismo: tenemos que crear una nueva instancia en la memoria de dicha clase.
Para ello, haremos lo siguiente:

Definimos una variable capaz de contener un objeto del tipo de la clase, esto lo haremos como con cualquier variable:

Dim cli As Cliente

Pero, a diferencia de las variables basadas en los tipos visto hasta ahora, para poder crear un objeto basado en una clase, necesitamos algo más de código que nos permita "crear" ese objeto en la memoria, ya que con el código usado en la línea anterior, simplemente estaríamos definiendo una variable que es capaz de contener un objeto de ese tipo, pero aún no existe ningún objeto en la memoria, para ello tendremos que usar el siguiente código:

cli = New Cliente()

Con esto le estamos diciendo al Visual Basic: crea un nuevo objeto en la memoria del tipo Cliente.

Estos dos pasos los podemos simplificar de la siguiente forma:

Dim cli As New Cliente()

A partir de este momento existirá en la memoria un objeto del tipo Cliente.

Nota:
En las versiones anteriores de Visual Basic no era recomendable usar esta forma de instanciar un nuevo objeto en la memoria, porque, aunque de forma transparente para nosotros, el compilador añadía código extra cada vez que se utilizaba esa variable, pero en la versión .NET no existe ese problema y por tanto no deteriora el rendimiento de la aplicación.

Y ahora nos queda saber cómo

Acceder a los miembros de una clase

Para acceder a los miembros de una clase (propiedades o métodos) usaremos la variable que apunta al objeto creado a partir de esa clase, seguida de un punto y el miembro al que queremos acceder, por ejemplo, para asignar el nombre al objeto cli, usaremos este código:

cli.Nombre = "Guillermo"

Es decir, de la misma forma que haríamos con cualquier otra variable, pero indicando el objeto al que pertenece dicha variable.
Como podrás comprobar, esto ya lo hemos estado usando anteriormente tanto en la clase Console como en las otras clases que hemos usado en entregas anteriores, incluyendo los arrays.

Y para acceder al método Mostrar:

cli.Mostrar()

Ves que fácil.

Pues así es todo lo demás en .NET Framework, así que... ¿para que seguir explicándote?

Bueno, vale... seguiremos viendo más cosas...

Espero que con lo dicho y lo que ya llevamos visto hasta ahora, tengas claro que son las propiedades y los métodos y si aún tienes dudas, no te preocupes ya que ahondaremos más en estos conceptos y sobre todo los utilizaremos hasta la saciedad, con lo cual puedes tener la tranquilidad de que acabarás enterándote al 100% de que va todo esto...

A pesar de que aún no estés plenamente informado de todo, vamos a ver un par de ejemplos de cómo usar las características de la programación orientada a objetos, después, en próximas entregas, profundizaremos más.

Ejemplo de cómo usar la herencia

Para poder usar la herencia en nuestras clases, debemos indicar al Visual Basic que esa es nuestra intención, para ello disponemos de la instrucción Inherits, la cual se usa seguida del nombre de la clase de la que queremos heredar. Veamos un ejemplo.

Empezaremos definiendo una clase "base" la cual será la que heredaremos en otra clase.

Ya sabemos cómo definir una clase, aunque para este ejemplo, usaremos la clase Cliente que vimos anteriormente, después crearemos otra, llamada ClienteMoroso la cual heredará todas las características de la clase Cliente además de añadirle una propiedad a esa clase derivada de Cliente.

Veamos el código de estas dos clases.
Para ello crea un nuevo proyecto del tipo consola y escribe estas líneas al principio o al final del fichero que el IDE añade de forma predeterminada.

Class Cliente
    Public Nombre As String

    Sub Mostrar()
        Console.WriteLine("El nombre del cliente: {0}", Nombre)
    End Sub
End Class

Class ClienteMoroso
    Inherits Cliente

    Public Deuda As Decimal

End Class

Como puedes comprobar, para que la clase ClienteMoroso herede la clase Cliente, he usado Inherits Cliente, con esta línea, (la cual debería ser la primera línea de código después de la declaración de la clase), le estamos indicando al VB que nuestra intención es poder tener todas las características que la clase Cliente tiene.
Haciendo esto, añadiremos a la clase ClienteMoroso la propiedad Nombre y el método Mostrar, aunque también tendremos la nueva propiedad que hemos añadido: Deuda.

Ahora vamos a ver cómo podemos usar estas clases, para ello vamos a añadir código en el procedimiento Main del módulo del proyecto:

Module Module1

    Sub Main()
        Dim cli As New Cliente()
        Dim cliM As New ClienteMoroso()
        '
        cli.Nombre = "Guille"
        cliM.Nombre = "Pepe"
        cliM.Deuda = 2000
        '
        Console.WriteLine("Usando Mostrar de la clase Cliente")
        cli.Mostrar()
        '
        Console.WriteLine("Usando Mostrar de la clase ClienteMoroso")
        cliM.Mostrar()
        '
        Console.WriteLine("La deuda del moroso es: {0}", cliM.Deuda)
        '
        Console.ReadLine()
    End Sub

End Module

Lo que hemos hecho es crear un objeto basado en la clase Cliente y otro basado en ClienteMoroso.
Le asignamos el nombre a ambos objetos y a la variable cliM (la del ClienteMoroso) le asignamos un valor a la propiedad Deuda.
Fíjate que en la clase ClienteMoroso no hemos definido ninguna propiedad llamada Nombre, pero esto es lo que nos permite hacer la herencia: heredar las propiedades y métodos de la clase base. Por tanto podemos usar esa propiedad como si la hubiésemos definido en esa clase. Lo mismo ocurre con los métodos, el método Mostrar no está definido en la clase ClienteMoroso, pero si que lo está en la clase Cliente y como resulta que ClienteMoroso hereda todos los miembros de la clase Cliente, también hereda ese método.

La salida de este programa sería la siguiente:

Usando Mostrar de la clase Cliente
El nombre del cliente: Guille
Usando Mostrar de la clase ClienteMoroso
El nombre del cliente: Pepe
La deuda del moroso es: 2000

Como puedes ver no es tan complicado esto de la herencia...

Ahora veamos cómo podríamos hacer uso del polimorfismo o al menos una de las formas que nos permite el .NET Framework.

Teniendo ese mismo código que define las dos clases, podríamos hacer lo siguiente:

    Sub Main()
        Dim cli As Cliente
        Dim cliM As New ClienteMoroso()
        '
        cliM.Nombre = "Pepe"
        cliM.Deuda = 2000
        cli = cliM
        '
        Console.WriteLine("Usando Mostrar de la clase Cliente")
        cli.Mostrar()
        '
        Console.WriteLine("Usando Mostrar de la clase ClienteMoroso")
        cliM.Mostrar()
        '
        Console.WriteLine("La deuda del moroso es: {0}", cliM.Deuda)
        '
        Console.ReadLine()
    End Sub

En este caso, la variable cli simplemente se ha declarado como del tipo Cliente, pero no se ha creado un nuevo objeto, simplemente hemos asignado a esa variable el contenido de la variable cliM.
Con esto lo que hacemos es asignar a esa variable el contenido de la clase ClienteMoroso, pero como comprenderás, la clase Cliente "no entiende" nada de las nuevas propiedades implementadas en la clase derivada, por tanto sólo se podrá acceder a la parte que es común a esas dos clases: la parte heredada de la clase Cliente.

Realmente las dos variables apuntan a un mismo objeto, por eso al usar el método Mostrar se muestra lo mismo. Además de que si hacemos cualquier cambio a la propiedad Nombre, al existir sólo un objeto en la memoria, ese cambio afectará a ambas variables.
Para comprobarlo, añade este código antes de la línea Console.ReadLine():

        Console.WriteLine()
        cli.Nombre = "Juan"
        Console.WriteLine("Después de asignar un nuevo nombre a cli.Nombre")
        cli.Mostrar()
        cliM.Mostrar()

 

La salida de este nuevo código, sería la siguiente:

Usando Mostrar de la clase Cliente
El nombre del cliente: Pepe
Usando Mostrar de la clase ClienteMoroso
El nombre del cliente: Pepe
La deuda del moroso es: 2000

Después de asignar un nuevo nombre a cli.Nombre
El nombre del cliente: Juan
El nombre del cliente: Juan

La parte en gris es lo que se mostraría antes de realizar el cambio.

Como puedes comprobar, al cambiar en una de las variables el contenido de la propiedad Nombre, ese cambio afecta a las dos variables, pero eso no es porque haya nada mágico ni ningún fallo, es por lo que te comenté antes: sólo existe un objeto en la memoria y las dos variables acceden al mismo objeto, por tanto, cualquier cambio efectuado en ellas, se reflejará en ambas variables por la sencilla razón de que sólo hay un objeto en memoria.

A este tipo de variables se las llama variables por referencia, ya que hacen referencia o apuntan a un objeto que está en la memoria. A las variables que antes hemos estado viendo se las llama variables por valor, ya que cada una de esas variables tienen asociado un valor que es independiente de los demás.
Recuerda que estas definiciones ya las vimos en la entrega número nueve.

 

Pues, vamos a dejarlo aquí... no sin resumir un poco lo que hemos tratado en esta entrega, de esta forma, si eres de los que empiezan a leer por el final, lo mismo te decides a leer desde el principio.

Hemos visto las condiciones que un lenguaje debe cumplir para que sea considerado orientado a objetos: herencia, encapsulación y polimorfismo. También hemos aprendido a definir una clase y a derivar una clase a partir de otra por medio de la instrucción Inherits. Ya sabemos cómo definir variables cuyo tipo sea una clase y cómo crear nuevos objetos en memoria a partir de una clase. También hemos comprobado que el que exista más de una variable del tipo de una clase no significa que exista un objeto independiente para cada una de esas variables y por último hemos comprobado que son las variables por referencia y cómo afectan a los objetos referenciados por ellas.


En la próxima entrega profundizaremos más en estos temas y así comprobarás que las cosas se pueden complicar aún mucho más... je, je.
También veremos, entre otras cosas, cómo podemos modificar o personalizar un método heredado para que se adapte a las nuevas características de la clase derivada, pero eso será en la próxima entrega. Mientras tanto te recomiendo que te leas la documentación de Visual Studio .NET y vayas profundizando en esto de la herencia y las clases.


Nos vemos.
Guillermo
Nerja, 17 a 18 de Febrero de 2003


ir a la entrega anterior ir al índice del curso vb.NET
 
ir al Glosario .NET
ir a la siguiente entrega (o al índice si esta es la última)

la Luna del Guille o... el Guille que está en la Luna... tanto monta...