Programación Orientada a Objetos con Visual Basic .NET
Parte II
Por Willy Mejía [WillyXoft]
POO con VB.NET
En primer lugar debemos tener en cuenta que todo en el .NET Framework, y por ende en Visual Basic .NET, puede ser
tratado como un objeto. Por ejemplo el tipo Integer ahora posee propiedaes y
métodos, tales como MaxValue que regresa el valor máximo que puede contener:
Dim i as Integer
MsgBox(i.MaxValue)
Con VB.NET observamos que tipos "básicos" como Integer se tratan de objetos. Del mismo modo, entidades
tales como los formularios y controles son todos instancias de alguna clase, es
decir:
objetos. Por ende una de las primeras cosas que debemos tomar en cuenta es que
el modo de tratar a éstos tipos a cambiado con respecto a VB6 y versiones
anteriores.
No obstante que todo puede ser tratado como un objeto, existen tipos por valor y por referencia. Los tipos por valor como
Integer no devuelven una referencia sino sólo el valor que contiene, y sus método en lugar de
modificar su estado o el valor contenido, sólo devuelven el valor modificado.
Definiendo Clases
Dado que los objetos se crean a partir de clases, son las clases las que
tenemos que diseñar y construir. Para ellos debemos recordar
que las clases se componen de los siguientes elementos o miembros:
- Atributos: Representan la información que contiene un objeto (estado).
- Métodos: Representan acciones que un objeto puede realizar.
Adicionalemente las clases puede definir Eventos, que son notificaciones que
un objeto recibe de, o transmite a, otros objetos, cuando se produce un
acontecimiento específico.
También existen los miembros compartidos de clase, que pueden utilizarse en instancias de una clase y en variables de
objeto declaradas como tipo de la clase. En otras palabras, no requieren una
instancia de clase en específico para realizar su cometido.
VB5 introdujo el concepto de módulo de clase en el cual podían definirse
clases simples, una por cada múdulo. Con VB.NET se pueden crear cualquier número
de clases en un mismo archivo, no obstante es una buena práctica el definir
clases en archivos separados. Para definir una clase se emplea, como entonces,
la palabra clave Class:
Class CCliente
'TODO: Miembros de clase
End Class
Si se requiere, también se pueden crear clases anidadas, es decir,
clases dentro de otras clases. Que resulta útil cuando cierto objeto va a
ser utilizado al interior de otro y nunca fuera del mismo.
Ahora como experimento abra el entorno de Visual Studio (o bien su Visual Basic .NET
Standard ó VB2005 Express) y
genere una nueva Aplicación Windows. Despues abra el archivo de código fuente
del formulario y observe que inicia con algo como:
Public Class Form1
Lo cual implica que el formulario se construye a partir de una Clase.
Incluso ahora los Modulos de código son clases, la única diferencia es
que se trata de Clases estáticas con mienbros compartidos.
Ahora pasemos a definir los miembros de la clase: los atributos y métodos
Atributos
Los atributos de clase se representan mediante Campos o Propiedades. Los
Campos se implementan mediante variables públicas que se pueden leer y
establecer directamente desde el exterior de la clase.
Public Nombre As String
Mientras que las Propiedades se recuperan y establecen
como los Campos, pero se implementan mediante procedimientos Property Get y
Property Set. El acceso a los atributos mediante procedimientos proporciona un mayor control sobre la forma en que se
establecen o se devuelven los valores de los atributos; esto ayuda a aislar los
datos y permite validar valores antes de ser asignados o recuperados.
Por ello también podriamos ver a las Propiedades como métodos de clase
ya que encajan en la definición y propósito de los mismos. De hecho en lenguajes
como Java no existen las propiedades y las operaciones de asignación (Set)
y obtención (Get) del valor de los atributos se realizan mediante métodos.
Se puede definir una propiedad de
la siguiente manera:
' Ahora el atributo es Privado
Private strNombre As String
' Para ser accesible mediante la Propiedad
Property Nombre() As String
Get
Return strNombre
End Get
Set(ByVal Value As String)
strName = Value
End Set
End Property
El procedimiento Get devuelve el valor de la propiedad, mientras que el
procedimiento Set le asigna el valor a la propiedad. Observe que dado que todo en .NET
es un objeto ya no es necesario la sentencia Let, así como que ambos
procedimientos están contenidos dentro una sola sentencia Property, lo cual
hace mas fácil de manipular y mantener el código de la Propiedad.
En aplicaciones de N-capas las clases generalmente se diseñan sin
estado, es decir sin propiedades.
Métodos
Los métodos de una clase en VB.NET se implementan mediante procedimientos o
subrutinas (Sub) y funciones (Function) declarados dentro de una clase, de la misma manera como se
realizaba anteriormente. No obstante ahora para devolver el valor desde una función
se utiliza la palabra clave Return en lugar del nombre de la misma, el cual
además se puede utilizar en los diferentes
puntos de salida de la función:
Function Hola() As String
If Nombre <> "" Then
Return "Hola " & Nombre
Else
Return "Hola Mundo"
End If
End Function
Aún se puede seguir utilizando, por compatibilidad hacia atrás, el
nombre de la función para devolver el valor. Mas es recomendable
utilizar Return dado que tiene un comportamiento más estructurado.
Constructores y Destructores
Los constructores y destructores son métodos especiales que como su nombre
indica, se emplean para la inicialización del objeto durante su construcción, y
la limpieza del mismo durante su destrucción.
En VB5 y VB6 las tareas de inicialización se implementan en el procedimiento
Initialize. Este procedimiento no permite el paso de parámetro alguno, lo cual
dificulta establecer el estado inicial del objeto durante su creación. Visual Basic .NET
sin embargo introduce verdaderos constructores que se defininen con el
procedimiento New:
Sub New()
'Código de Inicializacion
End Sub
Esta es la definición del constructor por omisión, la cual será
agregada por VB si no se define de manera explícita constructor alguno en una clase.
Dicho procedimiento permite la definicón de parámetros, lo que significa que pueden crearse constructores parametrizados.
Sub New(Nombre String)
strNombre = Nombre
End Sub
Con VB6 y anteriores los parámetros por omisión eran de paso por
referencia, con VB.NET ahora son por valor.
Y aun hay más, una clase puede tener cualquier cantidad de constructores, siempre y cuando
tengan diferente tipo y/o número de parámetros.
Sub New()
...
Sub New(Nombre As String)
...
Sub New(Nombre As String, Edad As Integer)
A ésta característica se le
conoce como sobrecarga de métodos de clase, por lo que se dice del método
que emplea la misma que es un método sobrecargado, y en el caso de una clase con
más de un constructor se dice que tiene un constructor sobrecargado.
En cuanto al destructor, Visual Basic .NET lo provee mediante el procedimiento Finalize
que no puede ser sobrecargado, es decir, que únicamente puede existir un sólo
destructor Finalize en cada clase.
Overrides Protected Sub Finalize()
Finalize está protegido (detalles sobre ésto mas adelante en
Encapsulación) lo que implica que no puede invocarse de manera directa por el
consumidor de la clase. La ejecución del código del destructor corre a cargo del
Colector de Basura del .NET Framework (GC: Garbage Collector)
una vez que el objeto pase a ser inaccesible. La excepción a la regla obedece al
hecho que todas las implementaciones de Finalize en una clase derivada
deben llamar a la correspondiente implementación de Finalize de la clase
base (eso implica Overrides que veremos más adelante en Herencia). Este es el único caso en el que se permite al código de la aplicación llamar a
Finalize.
Hay que tener mucho cuidado con las operaciones de Finalize, dado que
su ejecución corre a cargo del GC tienen las siguientes limitaciones:
- No se garantiza la liberación de recursos en un momento concreto; y
- No se garantiza que los finalizadores de dos objetos se ejecuten en un
orden determinado, aunque un objeto haga referencia al otro.
Por ello aquellas clases que requieran la liberación controlada de
recursos deben implementar la interface IDisposable e implementar el método
Dispose() con el código necesario para la liberación de los recursos. A
diferencia de Finalize, Dispose si puede invocarse de manera manual. Se abordará
el tema de Dispose mas adelante.
Creando y Destruyendo Objetos
Para crear un objeto, seguimos utilizando la cláusula New:
Private mCliente As CCliente
mCliente = New CCliente()
Observe que ya no se utiliza más la palabra Set al asignar la variable
al objeto, dado que ahora todo es un objeto ya no es necesaria la diferenciación
y se excluido del lenguaje.
También, como antes, se pueden realizar ambas operaciones en la misma línea:
Private mCliente As CCliente = New CCliente()
O incluso de la manera abreviada:
Private mCliente As New CCliente()
Aquí existe otra diferencia, con VB6 y anteriores este tipo de
asignación inicializaban el objeto en Nothing y creaba el objeto sólo hasta que
era utilizado, con VB.NET el objeto es creado desde el primer momento, cuando se
utiliza la cláusula New. Por ello se recomienda dejar de lado ésta práctica y
crear el objeto cuando se vaya a utilizar.
Si el constructor admite parámetros se colocan entre los paréntesis:
mCliente = New CCliente("Luz", 18)
Como hemos visto la destrucción del objeto corre a cargo del Recolector de
Basura (GC) por lo que no podemos destruir un objeto manualmente. Pero lo que sí
podemos hacer una vez utilizado el objeto, es invocar el método Dispose, si está
disponible en el objeto.
mCliente.Dispose()
No se confunda, al momento de invocar el método Dispose sólo se liberan
los recursos utilizados por el objeto, mas el objeto no es destruido.
Eventos
Se pueden utilizar los eventos para notificar a los objetos sobro la
ocurrencia de alguna situación especial. Los eventos se agregan a una clase con la instrucción Events. La
declaración incluye el nombre del evento y los argumentos que utiliza.
Event MiEvento(ByVal sender As System.Object, _
ByVal NumEvento As Integer)
Y al igual que antes, para conseguir que se produzca el evento, se debe utilizar la instrucción
RaiseEvent.
RaiseEvent MiEvento(Me, NumEvento)
Los eventos deben producirse dentro del ámbito en el que se han
declarado. Por ejemplo, una clase derivada no puede producir eventos heredados
de una clase base.
A diferencia de VB6 y anteriores, para consumir un evento se necesita asociar
de manera explícita el evento a un método (procedimiento) controlador de evento
(event handler) para lo cual se utiliza la cláusula Handles en
combinación con WithEvents.
Friend WithEvents Button1 As System.Windows.Forms.Button
Protected Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
End Sub
También se puede asociar un evento a un método controlador de evento con la
instrucción AddHandler que permite que un evento pueda asignarse a
cualquier número de controladores de eventos.
AddHandler MyObject.Event1, AddressOf Me.MyEventHandler
Además, los métodos que controlan determinados eventos pueden modificarse de
manera dinámica, si se utiliza en conjunto con la instrucción RemoveHandler,
que remueve la asociación entre un evento y su controlador.
RemoveHandler MyObject.Event1, AddressOf Me.MyEventHandler
Tanto AddHandler y RemoveHandler toman dos argumentos: el
nombre del evento, y una expresión que evalúa a un Delegado con la
instrucción AddressOf, la cual devuelve una referencia (apuntador) de un
Delegado. Pero eso se verá con detalle más adelante, por el momento sólo pruebe
el siguiente ejemplo:
Public Class ClasePublicadora
Public Event ElEvento()
Sub ProduceEvento()
RaiseEvent ElEvento()
End Sub
End Class
Public Class ClaseConsumidora
Sub TestEvento()
Dim Obj As New ClasePublicadora()
' Asociamos el evento con un controlador.
AddHandler Obj.ElEvento, AddressOf ControladorEvento
' Provocamos que se produzca el evento...
Obj.ProduceEvento()
' Removemos el controlador.
RemoveHandler Obj.ElEvento, AddressOf ControladorEvento
' Ya no pasa nada...
Obj.ProduceEvento()
End Sub
Sub ControladorEvento()
System.Windows.Forms.MessageBox.Write("Evento controlado.")
End Sub
End Class
Para mayor información al respecto léa:
Los delegados y el operador AddressOf.
Continuar con POO con Visual Basic .NET Parte 3