Sección de tutoriales y manuales vb

Tutorial sobre los módulos de clase

5 - Programación con sus propios objetos

Volver al índice



 

 

Tutorial realizado por JOEL ALEJANDRO

5 - Programación con sus propios objetos - Contenido

  1. Introducción
  2. Referencias de objeto y recuento de referencias
  3. Modelos de objetos
  4. Creación de sus propias clases de colección

 

 


1 - Introducción

 

Puede empezar a utilizar objetos de forma gradual, en las tareas en que sea conveniente la combinación de código y datos. Puede utilizar la funcionalidad de estos objetos si declara variables de objeto, les asigna nuevos objetos e invoca las propiedades y los métodos del objeto.

A medida que agrega más objetos a los programas, empezará a ver las relaciones que existen entre ellos. Puede empezar por diseñar programas más dependientes de los objetos y sus relaciones, y puede empezar a utilizar técnicas más sólidas, como la creación de clases de colección personalizadas, para expresar dichas relaciones en el código.

En algún momento observará que, repentinamente, la forma de relacionar los objetos modifica la naturaleza de los programas y estará en condiciones de empezar a diseñar programas basados en objetos desde los cimientos.

Los temas siguientes le proporcionan una introducción a estos cambios evolutivos de su estilo de escritura de código. Consúltelos ahora para hacerse una idea de dónde se encuentra y vuelva a leerlos cuando empiecen a asentarse sus ideas sobre la programación orientada a objetos.

 

Para obtener más información Los componentes ActiveX abren otra dimensión más en la reutilización de código y en la programación orientada a objetos. Si tiene la Edición profesional o la Edición empresarial de Visual Basic, puede empezar a explorar dicha dimensión en Creación de componentes ActiveX de la Guía de herramientas componentes.

 


2 - Referencias de objeto y recuento de referencias

 

La regla principal de la duración de un objeto es muy sencilla: un objeto se destruye cuando se libera la última referencia al mismo. Sin embargo, como todo en la vida, lo sencillo no siempre es fácil.

A medida que utiliza más objetos y va manteniendo más variables que contienen referencias a esos objetos, puede que se encuentre en la situación de parecerle imposible librarse de sus objetos cuando quiere.

En algún momento sucederá que Visual Basic tiene que efectuar el seguimiento de las referencias de objetos; de otro modo ¿cómo podría saber cuándo se libera la última referencia a un objeto? Puede que en ese momento piense que la depuración sería mucho más fácil si pudiera tener acceso a los recuentos de referencias de Visual Basic.

Desgraciadamente, no es cierto. Para que el uso de los objetos sea más eficiente, el Modelo de objetos componentes (COM) especifica una serie de accesos directos complejos para las reglas del recuento de referencias. El resultado es que no podría fiarse del recuento de referencias, incluso si tuviera acceso a él.

De acuerdo con las reglas de COM, la única información en la que puede confiar es si el recuento de referencias es o no cero. Puede saber cuándo el recuento de referencias llega a cero porque se produce el evento Terminate del objeto. Aparte de ésta, no hay más información fiable acerca del mismo.

Nota El hecho de no tener que recordar las reglas del recuento de referencias de COM no es poco. Administrar el recuento de referencias es mucho más difícil que realizar el seguimiento de las variables de objeto que contienen referencias a objetos.

Sugerencia Declare sus variables de objeto como tipos de clases, no As Object. De este modo, si tiene un objeto Widget que no termina, las únicas variables de las que tiene que preocuparse son las declaradas As Widget.

En las colecciones de referencias de objeto, no utilice el objeto Collection de Visual Basic por sí solo. Las referencias de objeto de un objeto Collection de Visual Basic se almacenan en variables Variant que, como las variables declaradas As Object, pueden contener referencias a objetos de cualquier clase. En su lugar, cree sus propias clases de colección que acepten objetos de una única clase, como se describe en “Creación de sus propias clases colección”. De esta manera, las únicas colecciones en las que tiene que buscar el objeto Widget son aquellas del tipo Widget.

Organice los objetos de forma jerárquica, como se describe en “Modelos de objetos”. Si todos los objetos están conectados entre sí, será fácil escribir un procedimiento que recorra todo el modelo e informe de los objetos existentes.

No declare variables As New. Son como las velas que se vuelven a encender después de haberlas apagado: si utiliza una después de haberla establecido a Nothing, Visual Basic crea inevitablemente otro objeto.

Para obtener más información Las referencias circulares son las más difíciles de deshacer de forma limpia. Consulte “Modelos de objetos”.

 


3 - Modelos de objetos

 

Después de definir una clase mediante la creación de un módulo de clase y la asignación de propiedades y métodos, puede crear cualquier número de objetos a partir de dicha clase. ¿Cómo puede efectuar el seguimiento de los objetos creados?

La manera más sencilla de efectuar el seguimiento de los objetos es declarar una variable de objeto por cada objeto que piensa crear. Naturalmente, esto supone un límite en cuanto al número de objetos que puede crear.

Puede mantener varias referencias de objeto en una matriz o en una colección, como se describe en “Creación de matrices de objetos” y “Creación de colecciones de objetos”, anteriormente en este tema.

Al principio, probablemente colocará las variables, las matrices y las colecciones de objetos en formularios o en módulos estándar, como hace con las variables normales. Pero cuando agregue más clases, descubrirá que los objetos que utiliza tienen claras relaciones entre ellos.

Los modelos de objetos definen relaciones de contenedores

Los modelos de objetos aportan la estructura a los programas basados en objetos. Cuando define las relaciones entre los objetos de un programa, el modelo de objetos organiza los objetos de forma que la programación sea más sencilla.

Normalmente, un modelo de objetos expresa el hecho de que unos objetos son "más grandes" o más importantes que otros. Se puede decir que estos objetos contienen otros objetos o que se componen de otros objetos.

Por ejemplo, puede crear un objeto PequeñaEmpresa como núcleo de su programa. Podría querer que el objeto PequeñaEmpresa tuviera asociados otros tipos de objetos, como objetos Empleado y objetos Cliente. Probablemente también querrá que contenga un objeto Producto. En la figura 9.11 se muestra un modelo de objetos para este programa.

Figura 9.11 Un modelo de objetos

 

Puede definir cuatro módulos de clase, llamados PequeñaEmpresa, Empleado, Cliente y Producto, y asignar a cada uno de ellos las propiedades y los métodos apropiados, pero ¿cómo conecta los objetos entre sí? Tiene dos herramientas para este propósito: las propiedades de objeto y el objeto Collection. El siguiente fragmento de código muestra una manera de implementar la jerarquía de la figura 9.11.

' Código de la sección Declaraciones del
' módulo de clase PequeñaEmpresa.
Public Nombre As String
Public Producto As New Producto
Public Empleados As New Collection
Public Clientes As New Collection

 

La primera vez que haga referencia a la propiedad Producto se creará el objeto, puesto que está declarada As New. Por ejemplo, con el código siguiente puede crear y establecer el nombre y el precio del objeto Producto del objeto PequeñaEmpresa.

' Código de un módulo estándar.
Public sbMain As New PequeñaEmpresa
Sub Main
	sbMain.Nombre = "Velociraptor Enterprises, Inc."
	' La primera vez que utiliza la variable Producto 
	' en el código se crea el objeto Producto.
	sbMain.Producto.Nombre = "Velociraptor hinchable"
	sbMain.Producto.Precio = 1.98
	.
	.	' Código para inicializar y mostrar el 
	.	' formulario principal.
	.
End Sub
 

 

Nota Implementar una propiedad de objeto con variables públicas es algo peligroso. Podría destruir el objeto Producto sin darse cuenta si establece la propiedad a Nothing en alguna parte del código. Es mejor crear las propiedades de objeto como propiedades de sólo lectura, como se muestra en el siguiente fragmento de código.

 

' Código de una propiedad objeto más robusta. El 
' almacenamiento es privado y así no se puede 
' establecer a Nothing desde fuera del objeto.
Private mProducto As New Producto

Property Get Producto() As Producto
	' La primera vez que invoca la propiedad, 
	' mProducto contiene Nothing, por lo que Visual Basic
	' creará un objeto Producto.
	Set Producto = mProducto
End If

 

Relaciones uno a varios

Las propiedades de objeto funcionan bien cuando la relación entre los objetos es de uno a uno. Sin embargo, ocurre con frecuencia que un objeto de un tipo contiene varios objetos de otro tipo. En el modelo de objetos PequeñaEmpresa, la propiedad Empleados está implementada como un objeto Collection para que el objeto PequeñaEmpresa pueda contener varios objetos Empleado. En el siguiente fragmento de código se muestra cómo puede agregar nuevos objetos Empleado a esta colección.

Public Function NuevoEmpleado(Nombre, Salario, 
FechaEntrada, ID) As Empleado
	Dim empNew As New Empleado
	empNew.Nombre = Nombre		' Creación implícita.
	empNew.Salario = Salario
	empNew.FechaEntrada = FechaEntrada
	' Agrega a la colección, con el Id. como clave.
	sbMain.Empleados.Add empNew, CStr(ID)
	' Devuelve una referencia al nuevo Empleado.
	Set NuevoEmpleado = empNew
End Function
 

 

La función NuevoEmpleado se puede llamar tantas veces como sea necesario para crear empleados en la empresa representada por el objeto PequeñaEmpresa. Los empleados existentes se pueden enumerar en cualquier momento recorriendo la colección Empleados.

Nota Una vez más, ésta no es una implementación muy sólida. Una forma mejor de programar sería crear sus propias clases de colección y exponerlas como propiedades de sólo lectura. Esto se describe en “Creación de sus propias clases de colección”.

Sugerencia El programa Generador de clases, incluido en la Edición profesional y en la Edición empresarial de Visual Basic, puede generar gran parte del código necesario para implementar un modelo de objetos. El Generador de clases crea robustas propiedades de objeto y clases de colección y le permite reorganizar su modelo con facilidad.

 

Propiedades Parent

Cuando tiene una referencia a un objeto, puede tener acceso a los objetos que contiene mediante las propiedades de objeto y las colecciones. También es muy útil poder recorrer la jerarquía para llegar al objeto que contiene el objeto al que se hace referencia.

El desplazamiento hacia arriba se suele efectuar con propiedades Parent. La propiedad Parent devuelve una referencia al contenedor del objeto. Para obtener una descripción del desplazamiento por el modelo de objetos, vea “Explorar los modelos de objetos” en “Programar con componentes”.

Puede encontrar un ejemplo de la propiedad Parent en “Agregar propiedades a una clase”, anteriormente en este tema.

Sugerencia Cuando asigne una propiedad Parent a un objeto de una colección, no utilice una referencia al objeto Collection. El objeto primario es realmente el objeto que contiene la colección. Si la propiedad Parent apunta a la colección, tendrá que utilizar dos niveles de indirección para llegar al objeto primario real; es decir, obj.Parent.Parent en vez de obj.Parent.

 

Propiedades Parent, referencias circulares y limpieza de objetos

Uno de los mayores problemas de las propiedades Parent es que pueden crear referencias circulares. El objeto "mayor" tiene una referencia al objeto que contiene y el objeto contenido tiene una referencia a aquél mediante su propiedad Parent, lo que crea un bucle como el que se muestra en la figura 9.12.

Figura 9.12 Un caso de referencias circulares

 

¿Dónde está el error en esta imagen? La manera de deshacerse de los objetos cuando ya no son necesarios es liberar todas las referencias a ellos. Si supone que la referencia al objeto PequeñaEmpresa se encuentra en una variable llamada sbMain, como en la sección anterior de este tema, puede escribir el código siguiente:

Set sbMain = Nothing

 

Desgraciadamente, todavía hay una referencia al objeto PequeñaEmpresa. De hecho, puede haber muchas referencias, porque cada propiedad Parent del objeto Empleado mantiene una referencia al objeto PequeñaEmpresa.
Como la colección Empleados del objeto PequeñaEmpresa mantiene una referencia a cada objeto Empleado, no se podrá destruir ninguno de los objetos.

 

Métodos de limpieza

Una solución consiste en incluir un método de limpieza en el objeto PequeñaEmpresa. Este método podría establecer todas las propiedades de objeto de PequeñaEmpresa a Nothing y todos los objetos Collection (Empleados, Clientes) a Nothing.

Cuando se destruye un objeto Collection, Visual Basic establece todas las referencias de objeto que contenía a Nothing. Si no hay más referencias a los objetos Empleado y Cliente contenidos en las colecciones Empleados y Clientes, se destruirán.

Desde luego, si el objeto Empleado se compone de objetos más pequeños, tendrá el mismo problema de referencia circular que el objeto primario. En ese caso, debe incluir un método de limpieza en la clase Empleado. En vez de establecer simplemente el objeto de colección Empleados a Nothing, el objeto PequeñaEmpresa recorrerá antes la colección y llamará al método de limpieza de cada objeto Empleado.

 

Todavía no hemos terminado

Incluso en esta situación puede que no se destruyan todos los objetos. Si en alguna parte del programa hay variables que siguen manteniendo referencias al objeto PequeñaEmpresa o a cualquiera de los objetos que contiene, dichos objetos no se destruirán. Una parte de la limpieza final del programa tiene que asegurarse de que todas las variables de objeto se establezcan a Nothing.

Para comprobar si esto ocurre, puede que tenga que agregar algo de código de depuración a los objetos. Por ejemplo, puede agregar el código siguiente a un módulo estándar:

' Colección global de depuración
Public gcolDebug As New Collection

' Función global para dar a cada objeto un Id. único.
Public Function DebugSerial() As Long
	Static lngSerial As Long
	lngSerial = lngSerial + 1
	DebugSerial = lngSerial
End Function

 

En cada módulo de clase puede colocar código similar al siguiente. Cada clase proporciona su propio nombre donde aparece “Producto”.

' Almacenamiento para el Id. de depuración.
Private mlngDebugID As Long

Property Get DebugID() As Long
	DebugID = mlngDebugID
End Property

Private Sub Class_Initialize()
	mlngDebugID = DebugSerial
	' Agrega una entrada de cadena a la colección 
	' global.
	gcolDebug.Add "Inicialización de Producto; DebubID=" _
	& DebugID, CStr(DebugID)
End Sub

Private Sub Class_Terminate()
	' Quita la entrada de cadena; así sabe que el 
	' objeto ha dejado de existir.
	gcolDebug.Remove CStr(DebugID)
End Sub

 

Cuando se crea un objeto, coloca una cadena en la colección global; cuando se destruye, quita la cadena. En cualquier momento puede recorrer la colección global para ver qué objetos no se han destruido.

Para obtener más información Los modelos de objetos adquieren una nueva importancia y otros problemas diferentes cuando utiliza la Edición profesional o la Edición empresarial de Visual Basic para crear componentes ActiveX. Vea “Principios generales del diseño de componentes” en la sección Crear componentes ActiveX de la Guía de herramientas componentes.

 


4 - Creación de sus propias clases de colección

 

Puede utilizar tres técnicas para implementar objetos contenedores mediante colecciones. Considere la colección Empleados del objeto PequeñaEmpresa descrito en “Modelos de objetos”.

Para implementar esta colección podría:

 

Estas estrategias se enumeran de menor a mayor robustez. Se podrían comparar con la casa de paja, la casa de madera y la casa de ladrillos.

 


4.1 - Ejemplo de colección pública: la casa de paja

 

Para crear el ejemplo, abra un nuevo proyecto e inserte dos módulos de clase. Dibuje cinco botones de comando, un cuadro de lista, dos cuadros de texto y dos etiquetas en el formulario, como se muestra en la figura 9.13.

Figura 9.13 Ejemplo de la colección Empleados

 

En la tabla siguiente se enumeran los valores de las propiedades que tiene que establecer en este ejemplo.

Objeto

Propiedad

Valor

     

Módulo de clase

Name

Empleado

Módulo de clase

Name

PequeñaEmpresa

Formulario

Caption

Colección Empleados

Primer botón de comando

Caption

Agregar

 

Name

cmdAddEmployee

Segundo botón de comando

Caption

Eliminar

 

Name

cmdDeleteEmployee

Tercer botón de comando

Caption

Actualizar lista

 

Name

cmdListEmployee

Cuarto botón de comando

Caption

Problema

 

Name

cmdTrouble

Quinto botón de comando

Caption

Cerrar

 

Name

cmdClose

Primera etiqueta

Caption

Nombre

Segunda etiqueta

Caption

Salario

Primer cuadro de texto

Name

txtName

 

Text

(vacío)

Segundo cuadro de texto

Name

txtSalary

 

Text

(vacío)

Cuadro de lista

Name

lstEmployees

 

En el módulo de clase Empleado, agregue las siguientes declaraciones y procedimientos de propiedad:

Option Explicit
' Propiedades de la clase Empleado.
Public Nombre As String
Public Salario As Long	

' Datos privados de la propiedad ID de una única 
' escritura.
Private mstrID As String

Property Get ID() As String
	ID = mstrID
End Property

' La primera vez que se establece la propiedad ID, 
' también se establece la variable booleana estática. 
' Las llamadas siguientes no hacen nada.
' (Sería mejor desencadenar un error.)
Property Let ID(strNew As String)
	Static blnAlreadySet As Boolean
	If Not blnAlreadySet Then
		blnAlreadySet = True
		mstrID = strNew
	End If
End Property 

 

La propiedad ID es la clave para obtener o eliminar un objeto Empleado de la colección, de modo que se establece una vez y ya no se modifica. Esto se consigue con la variable Static Boolean, que se establece a True la primera vez que se establece la propiedad. Siempre se puede leer la propiedad, puesto que hay un procedimiento Property Get.

En el módulo de clase PequeñaEmpresa, agregue la siguiente declaración. El objeto de colección se creará la primera vez que se haga referencia a la variable Empleados desde el código.

Option Explicit
Public Empleados As New Collection

 

El formulario hace todo el trabajo

Todo el resto del código se encuentra en el módulo de formulario. Agregue la siguiente declaración en la sección Declaraciones.

Option Explicit
Public sbMain As New PequeñaEmpresa

 

El código del evento cmdAddEmployee_Click agrega un miembro a la colección.

Private Sub cmdAddEmployee_Click()
	Dim empNew As New Empleado
	Static intEmpNum As Integer
	' El uso de With hace que el código sea más 
	' rápido y conciso (.ID en vez de empNew.ID).
	With empNew
		' Genera un Id. único para el nuevo empleado.
		intEmpNum = intEmpNum + 1
		.ID = "E" & Format$(intEmpNum, "00000")
		.Nombre = txtName.Text
		.Salario = CDbl(txtSalary.Text)
		' Agrega la referencia del objeto Empleado a la 
		' colección, con la propiedad ID como clave.
		sbMain.Empleados.Add empNew, .ID
	End With
	txtName.Text = ""
	txtSalary.Text = ""
	' Clic en el botón Actualizar lista.
	cmdListEmployees.Value = True
End Sub
 

 

El código del procedimiento de evento cmdListEmployees_Click utiliza una instrucción For Each ... Next para agregar toda la información de empleados al control Listbox.

Private Sub cmdListEmployees_Click()
	Dim emp As Empleado
	lstEmployees.Clear
	For Each emp In sbMain.Empleados
		lstEmpleados.AddItem emp.ID & ", " & _
		emp.Nombre & ", " & emp.Salario
	Next
End Sub 

 

El evento cmdDeleteEmployee_Click utiliza el método Remove del objeto Collection para eliminar el miembro de la colección actualmente seleccionado en el control ListBox.

Private Sub cmdDeleteEmployee_Click()
	' Comprueba que hay un empleado seleccionado.
	If lstEmployees.ListIndex > -1 Then
		' Los seis primeros caracteres son el Id.
		sbMain.Empleados.Remove _
		Left(lstEmployees.Text, 6)
	End If
	' Clic en el botón Actualizar lista.
	cmdListEmployees.Value = True
End Sub
	
Agregue el código siguiente al botón Problema.
Private Sub cmdTrouble_Click()
	' ¿Cómo dice?
	sbMain.Empleados.Add Me
End Sub 

 

El evento cmdClose_Click cierra la aplicación. Cuando cierre proyectos que utilizan objetos, descargue todos los formularios para asegurar que se ejecuten los procedimientos de evento Terminate de todos los módulos de clase. Por el contrario, el uso de la instrucción End termina el programa de forma brusca sin ejecutar los eventos Terminate.

Private Sub cmdClose_Click()
Unload Me
End Sub

 

Tan sólida como una casa de paja

Esta sencilla implementación no es muy robusta. Como la propiedad Empleados es un objeto público Collection, podría tener acceso a ella de forma inadvertida desde cualquier parte del programa. Además, el método Add del objeto Collection no comprueba el tipo. Por ejemplo, el código del evento Click del botón Problema inserta alegremente una referencia al formulario en la colección Empleados.

Haga clic en el botón Problema y observe que no se produce ningún error. Después haga clic en el botón Actualizar lista. Cuando el bucle For Each ... Next llega al objeto de tipo inesperado, provoca el error 13, "El tipo no coincide".

Éste es un ejemplo del tipo de error al que se expone cuando crea un modelo de objetos con objetos públicos Collection. Los objetos se pueden agregar desde cualquier parte del proyecto y no hay garantías de que se inicialicen correctamente. Si un programador copia el código con el que se agrega un empleado y después se modifica el código original, los errores que se provoquen serán muy difíciles de detectar.

Para obtener más información El ejemplo iniciado en este tema continúa en “Ejemplo de colección privada: la casa de madera”.

 


4.2 - Ejemplo de colección privada: la casa de madera

 

Este tema continúa el ejemplo de código iniciado en “Ejemplo de colección pública: la casa de paja”. Puede que le interese leer ese tema antes de empezar éste.

Una forma algo más sólida de vincular objetos Empleado con el objeto PequeñaEmpresa es hacer que el objeto Collection sea privado. En este ejemplo volverá a utilizar el formulario y la mayor parte del código del ejemplo de la colección pública.

El módulo de clase Empleado sigue igual. Sin embargo, el módulo de clase PequeñaEmpresa cambia de cara totalmente. Substituya la declaración del objeto público Collection por la siguiente declaración y agregue los procedimientos Sub y Function descritos en los párrafos siguientes.

Option Explicit
Private mcolEmpleados As New Collection

 

Como antes, el código con el que se agrega un empleado efectúa casi todo el trabajo. (Puede aprovechar el bloque de código entre las líneas de puntos del procedimiento de evento cmdEmployeeAdd_Click del ejemplo anterior.)

El cambio más importante es que el método Add del objeto Collection ya no se puede invocar desde cualquier módulo del programa, puesto que colEmpleados es Private. Sólo puede agregar un objeto Empleado mediante el método AgregarEmpleado, que inicializa correctamente el nuevo objeto:

 

' Método de la clase PequeñaEmpresa.
Public Function AgregarEmpleado(ByVal Name As String, _
ByVal Salary As Double) As Empleado
	' - - - - - - - - - - - - - - - -
	Dim empNew As New Empleado
	Static intEmpNum As Integer
	' El uso de With hace que el código sea más 
	' rápido y conciso (.ID en vez de empNew.ID).
	With empNew
		' Genera un Id. único para el nuevo empleado.
		intEmpNum = intEmpNum + 1
		.ID = "E" & Format$(intEmpNum, "00000")
		.Nombre = Name
		.Salario = Salary
		' Agrega la referencia del objeto Empleado a la 
		' colección, con la propiedad ID como clave.
		' - - - - - - - - - - - - - - - -
		mcolEmpleados.Add empNew, .ID
	End With
	' Devuelve una referencia al nuevo objeto Empleado.
	Set AgregarEmpleado = empNew
End Function
 

 

El método AgregarEmpleado devuelve una referencia al objeto Empleado recién agregado. Ésta es la forma correcta de programar, porque después de crear un objeto se suele querer hacer algo con él.

Los métodos ContarEmpleados, EliminarEmpleado y Empleados delegan en los métodos correspondientes del objeto Collection. La delegación significa que el objeto Collection realiza todo el trabajo.

' Métodos de la clase PequeñaEmpresa.
Public Function ContarEmpleados() As Long
	ContarEmpleados = mcolEmpleados.Count
End Function

Public Sub EliminarEmpleados(ByVal Index As Variant)
	mcolEmpleados.Remove Index
End Sub

Public Function Empleados(ByVal Index As Variant) _
As Empleado
	Set Empleados = mcolEmpleados.Item(Index)
End Function 

 

Nota Puede agregar funcionalidad adicional a estos métodos. Por ejemplo, puede desencadenar errores si el índice no es válido.

El último método es Problema. Este método intenta agregar a la colección un objeto Empleado no inicializado. ¿Qué puede pasar?

' Método de la clase PequeñaEmpresa.
Public Sub Problema()
	Dim x As New Empleado
	mcolEmpleados.Add x
End Sub 

 

Modificaciones en el formulario

Tendrá que efectuar algunas modificaciones en el módulo de formulario. Puede utilizar las declaraciones de nivel de módulo empleadas en el ejemplo anterior y el evento Click del botón Cerrar es el mismo, pero se han cambiado otros procedimientos de evento: el código del botón Agregar es mucho más breve, mientras que el código de los botones Eliminar y Actualizar lista ha cambiado poco pero de forma muy significativa:

Private Sub cmdAddEmployee_Click()
	sbMain.AgregarEmpleado txtName.Text, txtSalary.Text
	txtName.Text = ""
	txtSalary.Text = ""
	cmdListEmployees.Value = True
End Sub

Private Sub cmdDeleteEmployees_Click()
	' Verifica que hay un empleado seleccionado.
	If lstEmployees.ListIndex > -1 Then
		' Los seis primeros caracteres son el Id.
		sbMain.EliminarEmpleado Left(lstEmployees.Text, 
		6)
	End If
	cmdListEmployees.Value = True
End Sub

Private Sub cmdListEmployees_Click()
	Dim lngCt As Long
	lstEmployees.Clear
	For lngCt = 1 To sbMain.ContarEmpleados
		With sbMain.Empleados(lngCt)
			lstEmployees.AddItem .ID & ", " & .Nombre _
			& ", " & .Salario
		End With
	Next
End Sub
 

 

¿Qué es todo este código extra de cmdListEmployees_Click? Desgraciadamente, en favor de la solidez ha perdido la posibilidad de utilizar For Each ... Next para recorrer los elementos de la colección, ya que el objeto Collection está declarado como Private. Si intenta escribir lo siguiente, recibirá un error:

' No funciona, porque Empleados no es una
' colección.

For Each emp In sbMain.Empleados

 

Afortunadamente, el método ContarEmpleados se puede utilizar para delimitar el intervalo de iteración.

El botón Problema también cambia un poco, pero sigue siendo lo que es, un problema.

Private Sub cmdTrouble_Click()
	sbMain.Problema
End Sub 

 

Ejecute el proyecto y experimente con los botones Agregar, Eliminar y Actualizar lista. Todo funciona igual que antes.

Cuando hace clic en el botón Problema tampoco se generan errores. Sin embargo, si hace clic en el botón Actualizar lista, puede ver que de alguna manera se ha agregado a la colección el objeto Empleado no inicializado.

¿Cómo ha podido ser? Al crear el objeto Collection como privado, lo protegió del código del programa que está fuera del objeto PequeñaEmpresa, pero no del código de dentro. El objeto PequeñaEmpresa puede ser grande y complejo, con una gran cantidad de código. Por ejemplo, probablemente tiene métodos como AgregarCliente, AgregarProducto, etc.

Un error de escritura de código o la creación de un duplicado del método AgregarEmpleado puede hacer que se inserten en la colección datos incorrectos e incluso objetos no válidos, ya que la variable privada es visible en todo el módulo de clase.

Para obtener más información Este ejemplo continúa en “Creación de su propia clase de colección: la casa de ladrillos”.

 


4.3 - Creación de su propia clase de colección: la casa de ladrillos

 

Este tema continúa el ejemplo de código iniciado en “Ejemplo de colección pública: la casa de paja” y en “Ejemplo de colección privada: la casa de madera”. Puede que le interese ver esos temas antes de empezar éste.

La manera más robusta de implementar una colección es convertirla en un módulo de clase. En contraste con los ejemplos anteriores, pasar todo el código de la creación del objeto a la clase de colección cumple los principios del buen diseño de objetos.

En este ejemplo se utiliza el mismo formulario y el mismo módulo de clase Empleado que en los ejemplos anteriores. Inserte un nuevo módulo de clase y establezca su propiedad Name a “Empleados”. Inserte las declaraciones y el código siguientes en el nuevo módulo de clase.

Option Explicit
Private mcolEmpleados As New Collection

 

Los métodos Add, Count y Delete de la clase Empleados son esencialmente los mismos que los de la antigua clase PequeñaEmpresa. Puede quitarlos del módulo de clase PequeñaEmpresa, pegarlos en el módulo de clase Empleados y cambiar sus nombres.

Los nombres pueden cambiar porque ya no es necesario distinguir AgregarEmpleado de, por ejemplo, AgregarCliente. Cada clase colección que implemente tiene su propio método Add.

' Métodos de la clase colección Empleados.
Public Function Add(ByVal Name As String, _
ByVal Salary As Double) As Empleado
	Dim empNew As New Empleado
	Static intEmpNum As Integer
	' El uso de With hace que el código sea más 
	' rápido y conciso (.ID en vez de empNew.ID).
	With empNew
		' Genera un Id. único para el nuevo empleado.
		intEmpNum = intEmpNum + 1
		.ID = "E" & Format$(intEmpNum, "00000")
		.Nombre = Name
		.Salario = Salary
		' Agrega la referencia del objeto Empleado a la 
		' colección, con la propiedad ID como clave.
		mcolEmpleados.Add empNew, .ID
	End With
	' Devuelve una referencia al nuevo objeto Empleado.
	Set Add = empNew
End Function

Public Function Count() As Long
	Count = mcolEmpleados.Count
End Function

Public Sub Delete(ByVal Index As Variant)
	mcolEmpleados.Remove Index
End Sub 

 

El método Empleados del objeto PequeñaEmpresa se convierte en el método Item de la clase de colección. Sigue delegando en el objeto Collection para poder obtener los miembros por índice o por clave.

' Método de la clase colección Empleados.
Public Function Item(ByVal Index As Variant) _
As Empleado
	Set Item = colEmpleados.Item(Index)
End Function 

 

Aquí puede agregar un toque de estilo. Si hace que Item sea el método predeterminado de la clase Empleados, tendrá la posibilidad de escribir Empleados("E00001"), igual que podía hacerlo con el objeto Collection.

 

Para hacer de Item la propiedad predeterminada

 

Nota Una clase sólo puede tener un miembro predeterminado (una propiedad o un método).

 

Activación de For Each … Next

Junto con la solidez, vuelve a disponer de For Each … Next. Una vez más, puede delegar todo el trabajo en el objeto Collection si agrega el método siguiente:

' NewEnum tiene que devolver la interfaz IUnknown del
' enumerador de una colección.
Public Function NewEnum() As IUnknown
	Set NewEnum = mcolEmpleados.[_NewEnum]
End Function

 

Lo más importante que delega en el objeto Collection es el enumerador. El enumerador es un pequeño objeto que sabe cómo recorrer los elementos de una colección. No puede escribir un objeto enumerador con Visual Basic, pero como la clase Empleados está basada en un objeto Collection, puede utilizar el enumerador del objeto Collection, que, naturalmente, sabe de sobra cómo enumerar los elementos del objeto Collection que mantiene.

Los corchetes que enmarcan el método _NewEnum del objeto Collection son necesarios a causa del subrayado inicial del nombre del método. Este subrayado inicial es una convención que indica que el método se encuentra oculto dentro de la biblioteca de tipos. No puede asignar un nombre al método _NewEnum, pero puede ocultarlo en la biblioteca de tipos y darle el Id. de procedimiento que requiere For EachNext.

Para ocultar el método NewEnum y darle el Id. de procedimiento necesario

 

Importante Para que las clases colección acepten For Each … Next, tiene que proporcionar un método NewEnum oculto con el Id. de procedimiento correcto.

No queda mucho de la clase PequeñaEmpresa

La clase PequeñaEmpresa tiene ahora mucho menos código. Para sustituir el objeto Collection y todos los métodos quitados hay una nueva declaración y una propiedad de sólo lectura:

Option Explicit
Private mEmpleados As New Empleados

Public Property Get Empleados() As Empleados
	Set Empleados = mEmpleados
End If 

 

Esto merece una explicación. Suponga por un momento que quita el procedimiento Property Get y simplemente declara Public Empleados As New Empleados.

Todo funciona bien mientras nadie cometa errores, pero ¿qué pasa si escribe accidentalmente Set sbMain.Empleados = Nothing? Eso es, se destruirá la colección Empleados. Si convierte Empleados en una propiedad de sólo lectura, evita dicha posibilidad.

 

Modificaciones al formulario

El código del módulo de formulario es muy parecido al del ejemplo anterior. Puede utilizar las mismas declaraciones de nivel de módulo y el evento Click del botón Cerrar es el mismo.

El único cambio en la mayoría de los procedimientos de evento es que se reemplazan los antiguos métodos de la clase PequeñaEmpresa por los nuevos métodos del objeto colección Empleados:

 

Private Sub cmdAddEmployee_Click()
	sbMain.Empleados.Add txtName.Text, txtSalary.Text
	txtName.Text = ""
	txtSalary.Text = ""
	cmdListEmployees.Value = True
End Sub

Private Sub cmdDeleteEmployee_Click()
	' Comprueba que hay un empleado seleccionado.
	If lstEmpleados.ListIndex > -1 Then
		' Los seis primeros caracteres son el Id.
		sbMain.Empleados.Delete _
		Left(lstEmployees.Text, 6)
	End If
	cmdListEmployees.Value = True
End Sub

Private Sub cmdListEmployees_Click()
	Dim emp As Empleado
	lstEmployees.Clear
	For Each emp In sbMain.Empleados
		lstEmployees.AddItem emp.ID & ", " & _ 
		emp.Nombre & ", " & emp.Salario
	Next
End Sub 

 

Observe que otra vez puede utilizar For Each … Next para enumerar los empleados.

Ejecute el proyecto y compruebe que todo funciona bien. Esta vez no hay código en el botón Problema, puesto que el encapsulamiento impide que haya problemas.

Para obtener más información Para obtener un mayor conocimiento de las colecciones, consulte “El objeto Collection de Visual Basic” y “Colecciones de Visual Basic”.

El programa Generador de clases incluido en la Edición profesional y en la Edición empresarial le ayudará a crear clases de colección.

Las lecciones de los ejemplos de la casa de paja, la casa de madera y la casa de ladrillos se resumen en “Ventajas del correcto diseño orientado a objetos”.

 


4.4 - Ventajas del correcto diseño orientado a objetos

 

En este tema se resumen los resultados del ejemplo de código iniciado en “Ejemplo de colección pública: la casa de paja” y continuado en “Ejemplo de colección privada: la casa de madera” y “Creación de su propia clase colección: la casa de ladrillos”. Puede que le interese ver esos temas antes de empezar este.

La creación de la clase de colección Empleados da como resultado un estilo de escritura de código muy claro y modular. Todo el código de la colección está en la clase de colección (encapsulamiento), lo que reduce el tamaño del módulo de clase PequeñaEmpresa. Si aparecen colecciones de objetos Empleado en más de un lugar de la jerarquía de objetos, volver a utilizar la clase colección no requiere la duplicación del código.

 

Mejoras de las clases de colección

Puede implementar métodos y propiedades adicionales para las clases colección. Por ejemplo, puede implementar métodos Copy y Move o una propiedad Parent de sólo lectura que contenga una referencia al objeto PequeñaEmpresa.

También puede agregar un evento. Por ejemplo, cada vez que el método Add o el método Remove modifica el número de elementos de la colección, puede desencadenar un evento CountChanged.

Solidez, solidez y solidez

No siempre tiene que implementar las colecciones de la manera más sólida posible. Sin embargo, una de las ventajas de programar con objetos es la reutilización del código; es mucho más fácil reutilizar objetos que copiar código de origen y es mucho más seguro utilizar código sólido y encapsulado.

Como dijo un sabio, “Si quiere escribir código realmente robusto es porque asume que se le van a presentar problemas”.

 

Clases de colección y software componente

Si utiliza la Edición profesional o la Edición empresarial de Visual Basic, puede convertir su proyecto en un componente ActiveX, de modo que otros programadores de su organización puedan utilizar los objetos que ha creado.

 

Pasos para implementar una clase de colección

En la lista siguiente se resumen los pasos requeridos para crear una clase colección.

1. Agregue un módulo de clase al proyecto y asígnele un nombre (normalmente el plural del nombre del objeto que va a contener la clase colección). (Consulte “Asignación de nombres de propiedades, métodos y eventos”, anteriormente en este tema.)

2. Agregue una variable privada para incluir una referencia al objeto Collection en el que van a delegar las propiedades y los métodos.

3. En el procedimiento de evento Class_Initialize, cree el objeto Collection. (Si quiere retrasar la creación de este objeto hasta cuando se vaya a utilizar, puede declarar la variable privada del paso 2 como As New Collection. Esto agrega un poco más de proceso cada vez que se tiene acceso al objeto Collection.)

4. Agregue al módulo de clase una propiedad Count y métodos Add, Item y Remove; en cada caso, delegue en el objeto privado Collection mediante una llamada a su miembro correspondiente.

5. Cuando implemente el método Add, puede eludir el comportamiento no discriminatorio del método Add del objeto Collection para aceptar solamente objetos de un tipo. Puede incluso impedir que se agreguen a la colección objetos creados externamente, de forma que el método Add controle completamente la creación e inicialización de los objetos.

6. Utilice el cuadro de diálogo Atributos del procedimiento para que el método Item sea el predeterminado de la clase de colección.

7. Agregue un método NewEnum, como se muestra a continuación. Utilice el cuadro de diálogo Atributos del procedimiento para marcarlo como oculto y asignarle un Id. de procedimiento de –4, para poder utilizar For Each … Next.

Public Function NewEnum() As IUnknown
Set NewEnum = mcol.[_NewEnum]
End Function

 

Nota En este código se asume que la variable privada del paso 2 se llama mcol.

8. Agregue propiedades, métodos y eventos personalizados a la clase de colección.

Nota El programa Generador de clases, incluido en la Edición profesional y en la Edición empresarial de Visual Basic, le ayudará a crear estas clases de colección. Puede personalizar el código de origen que produce.

Para obtener más información Puede encontrar más información acerca de los componentes software en la sección Creación de componentes ActiveX de la Guía de

 


Volver al índice

 

 


Buscar en Recursos vb