Programación Orientada a Objetos en Visual Basic

Anuncio
Programación Orientada a Objetos en Visual Basic
Resumen
La programación en Visual Basic se puede realizar de diversas formas o estilos. Este lenguaje nace
como un ambiente de prototipación rápida (RAD) para aplicaciones bajo el sistema operativo
Microsoft Windows. La orientación a objetos se incorpora como un elemento posterior al
nacimiento del lenguaje y actualmente (versión 5) no está totalmente soportada.
A pesar de lo anterior, Visual Basic permite la aplicación de los conceptos de orientación a objetos,
aunque esto no corresponda a un mapeo directo. Acá se presenta una técnica que permite programar
en forma estándar, basándose un modelo de clases UML. Por otra parte los conceptos que se
plantean son fácilmente aplicables a otros lenguajes de programación visuales.
Además de cumplir con el paradigma de la orientación a objetos, se presentan acá formas de
programación que cumplen con una arquitectura de tres capas. De esta forma, la lógica, en
términos de reglas del negocio, se aisla de los elementos de interfaz y de la forma de
almacenamiento de los objetos. Bajo este tipo de arquitectura, es posible separar físicamente estos
componentes del negocio, pudiéndose centralizar en servidores especializados, a través del uso de
monitores transaccionales como Microsoft MTS.
Arquitectura de 3 capas.
El objetivo principal de este planteamiento es separar y, por lo tanto independizar, las reglas del
negocio de las aplicaciones que las utilicen. De esta forma, es posible utilizar las mismas reglas del
negocio para diferentes aplicaciones, corriendo sobre distintas plataformas.
La capa de aplicación corresponde a programas interactivos o procesos, que
realicen alguna acción sobre los componentes del negocio. La capa
Capa de
intermedia estará formada por las clases del negocio, quienes interactúan con
Aplicación
el medio de almacenamiento que está repreentado en la capa de datos.
Una arquitectura pura de tres capas no permite ninguna forma de
comunicación directa entre la aplicación y la capa de datos; esto es, entre los
Capa del
programas de usuario y la base de datos. Los programas de aplicación deben
Negocio
solicitar todos sus requerimientos a la capa del negocio, a través de la
invocación de servicios, que corresponden a los métodos definidos para cada
clase.
La separación propuesta por este modelo de capas nos da una clara guía de la
Capa de Datos
forma en que podemos enfrentar la programación de un sistema en Visual
Basic.
Algunos elementos del lenguaje
Uso de propiedades en Visual Basic.
Una propiedad se define en algunos lenguajes visuales como un atributo definido en una clase, para
el cual existen funciones de entrada y salida. Esto nos permite presentar como servicios, atributos
que se puedan derivar de fórmulas o acceso a base de datos.
Supongamos el siguiente ejemplo para una clase artículo:
Public Nombre As String
Public Valor As Double
Public Property Get ValorConIva as Double
ValorConIva = Valor * 1.18
1
End Property
Para un objeto de la clase artículo, se podrá consultar directamente Articulo.ValorConIva.
Además del Property Get, se cuenta con un Property Let que permite asignar valores a variables y
un Property Set para asignar referencias a objetos.
Objetos y referencias a objetos.
Visual Basic permite la definición de módulos de clases. En cada uno de ellos se escribe la
definición de las variables y métodos públicos y privados de cada una de las clases. Esta definición
de clases actúa luego como un nuevo tipo de dato para el que se pueden definir variables.
Por ejemplo, si ya tenemos creada una clase Artículo, podemos definir una variable de ese tipo.
Dim Articulo As Articulo
Con esta definición solo creamos una referencia a un Artículo, aún no se ha creado realmente un
objeto en memoria. El nombre de la variable puede (en Visual Basic) ser el mismo que el de la
clase.
Para crear un nuevo objeto y referenciarlo desde la variable Artículo, se utiliza la sentencia New, de
la forma.
Set Articulo = New Articulo
Esto se podría haber hecho directamente en la declaración:
Dim Articulo As New Articulo
La palabra reservada Set se utiliza para realizar asignaciones de referencias a objetos. Los objetos
que se crean se mantienen en memoria hasta que se elimina la última referencia hacia ellos. La
eliminación de las referencias se hace asignándoles la constante Nothing.
Set Articulo = Nothing
Cuando un objeto es retornado por algún servicio no es necesario crearlo antes de llamarlo. El
código dentro del método debe crearlo antes de retornarlo.
Dim Movimiento As Movimiento, Bodega As New Bodega
Set Movimiento = Bodega.BuscaPrimerMovimiento(Now, Now-6)
Set Movimiento = Nothing
Public Function Bodega.BuscaPrimerMovimiento(FechaInicio As Date,
FechaTermino As Date) As Movimiento
Dim Mov As New Movimiento
‘ ... Conectar a B.D. y buscar el 1º movimiento
Set BuscaPrimerMovimiento = Mov ‘ Retornar Mov
Set Mov = Nothing
End Function
Al asignarle Nothing a Mov, el objeto no se destruye ya que queda una referencia activa hacia él
(BuscaPrimerMovimiento).
Uso de colecciones
Una colección es un tipo de objeto definido en el lenguaje. Este objeto es capaz de contener otras
variables tipo Variant, las que a su vez pueden ser otros objetos.
Los servicios que contiene una colección son Add, Remove y Count. Además es posible buscar
elementos dentro de una colección a través de una clave que se debe dar en el momento en que
éstos se agregan (como parámetro en el método Add).
Otro servicio especial de las colecciones es un iterador el que permite recorrer sus objetos mediante
sentencias “for each”. Por ejemplo:
Dim A1 As New Articulo, A2 As New Articulo, A3 As New Articulo
‘.. Inicialización de A1,A2 y A3.
Dim Col As New Collection
2
Dim A As Articulo
Col.Add A1
Col.Add A2
Col.Add A3
Set A1 = Nothing
Set A2 = Nothing
Set A3 = Nothing
For Each A In Col
Suma = Suma + A.Valor
Next
Set Col = Nothing
La variable A no se inicializa (New), ya que se le asignan referencias a objetos que ya existen en la
colección. Por otra parte, las variables A1, A2 y A3 se pueden asignar a Nothing sin que se
destruyan los objetos, porque estos ya están creados dentro de la colección y serán destruidos sólo
cuando ésta se destruya (Set Col = Nothing).
Conexión desde Visual Basic a la Base de Datos.
Existen varias formas de conectar una aplicación Visual Basic a un servidor de base de datos. Acá
se presenta la bilioteca Remote Data Objects 2.0 (rdo).
Para que un proyecto en Visual Basic pueda utilizar los objetos de rdo, es necesario referenciar a
esta biblioteca. Esto se hace agregando una referencia desde la opción “Proyecto|Referencias”
hacia “Microsoft Remote Data Objects 2.0”. Esta bilioteca utiliza fuentes de dato definidas en el
sistema operativo (Panel de Control – ODBC).
Esta bilioteca de objetos para acceso a datos remotos, se distribuye con la versión Enterprise de
Visual Basic 5.0.
Si tenemos una fuente de datos llamada “Bodega” que apunta a una base de datos en Microsoft SQL
Server, en la que hay un usuario con identificación “sa” y sin clave de acceso, la cadena de
conexión hacia esa fuente de datos está dada por:
“dsn=Bodega;uid=sa;pwd=”
Esta cadena de conexión indica los parámetros para que un objeto rdoConnection se conecte a la
base de datos requerida.
Los objetos más utilizados de la biblioteca rdo son rdoConnection, rdoQuery y rdoResultSet.
Supongamos que en la base de datos de bodega existe una tabla llamada “Bodega” con un campo
código y uno descripción. El código necesario para encontrar una bodega es el siguiente:
Dim Conex As New rdoConnection
Dim Q As New rdoQuery
Dim R As rdoResultset
Conex.Connect = "dsn=Bodega;uid=sa;pwd="
On Error GoTo ErrorEnConexion
Conex.EstablishConnection rdDriverNoPrompt
On Error GoTo 0
Set Q.ActiveConnection = Conex
Q.SQL = "Select * from Bodega where Codigo = ?"
3
Q(0).Type = rdTypeCHAR
Q(0).Value = "B01"
Set R = Q.OpenResultset
If Not R.EOF Then
MsgBox "La bodega es " & R.rdoColumns("Descripcion").Value
Else
MsgBox "No se encontró la bodega"
End If
Set R = Nothing
Set Q = Nothing
Set Conex = Nothing
Exit Sub
ErrorEnConexion:
MsgBox "Error al conectarse: " & Err.Description
A un objeto Query es posible indicarle una consulta con parámetros, como en el ejemplo anterior.
En este caso, se le debe indicar un valor a cada uno de ellos y es conveniente también indicarle un
tipo de dato definido en ODBC. La asignación del tipo y valor se realiza directamente sobre el
objeto Query, usando el orden del parámetro como sub índice que comienza desde cero. En el
ejemplo anterior, Q(0).Type y Q(0).Value.
Es posible recorrer conjuntos de resultados provenientes de una consulta empleando el servicio
MoveNext de los objetos ResultSet, en conjunto con el método EOF.
Programación de la Capa del Negocio.
La capa del negocio contendrá las clases del sistema y no interactuará directamente con los
usuarios. Por ello, en el código que se programe para representar a las clases y sus servicios no se
deben realizar llamadas a funciones que resulten en efectos visuales o interactivos, como abrir
formularios o mensajes de aviso.
Se usará el siguiente ejemplo para demostrar los diferentes aspectos de la programación orientada a
objetos en Visual Basic.
4
Modelo de Clases
5
Modelo de Datos E-R
6
Debido a que se programa una capa intermedia que se desea utilizar desde diferentes tipos de
aplicaciones, desde Visual Basic creamos un nuevo proyecto del tipo “ActiveX dll”. De esta forma
las clases públicas que se definan en él podrán ser creadas y utilizadas desde otros proyectos Visual
Basic, desde macros Word o Exel, desde páginas Web vía ASP, o cualquier ambiente que soporte
el estándar ActiveX.
Por cada una de las clases definidas en el sistema, se crea su correspondiente módulo de clases en el
proyecto que creamos. El nombre de las clases no puede ser igual al nombre del proyecto.
En cada módulo de clases definiremos variables públicas, variables privadas, propiedades y
métodos.
En primer lugar, en cada clase podemos declarar una variable para mantener una referencia a la
conexión a la Base de Datos. Declaramos entonces al inicio de cada clase:
Dim Conex As rdoConnection
Esta variable deberá ser asignada desde la aplicación u otras clases cada vez que se creen nuevos
objetos y se usen sus servicios.
Por cada uno de los atributos definidos en el modelo de clases, podemos declarar una variable
pública en el módulo de la clase correspondiente.
Por otro lado, por cada uno de los campos de la tabla asociada en el modelo de datos que se utilice
como clave foránea hacia otra tabla, declaramos atributos privados. La declaración de variables de
la clase Movimiento quedaría entonces como:
‘ Conexión a Base de Datos
Public Conex As rdoConnection
‘ Atributos públicos de la clase
Public FechaHora As Date
Public Cantidad As Long
‘ Atributos privados para representar relaciones
Private BodegaCodigo As Long
Private ArticuloCodigo As Long
Las clases se instanciarán (asignarán valores a sus variables) desde objetos del tipo ResultSet.
Podemos entonces crear en todas las clases un servicio public que instancie sus atributos desde un
ResultSet abierto y posicionado sobre una fila. Escribimos entonces para cada una de las clases un
servicio “LlenaDesdeResultSet”.
En el caso de la clase Movimiento, el servicio sería:
Public Sub LlenaDesdeResultSet(ByRef RS As ResultSet)
If Not RS.EOF Then
FechaHora = RS.rdoColumns(“FechaHora”)
Cantidad = RS.rdoColumns(“Cantidad”)
BodegaCodigo = RS.rdoColumns(“BodegaCodigo”)
ArticuloCodigo = RS.rdoColumns(“ArticuloCodigo”)
End If
End Sub
Cada una de las clases necesitará crear, destruir, modificar y leer sus objetos. Por ello podemos
crear estos cuatro servicios para todas las clases, aunque no estén definidos en el modelo. En el
caso de la clase Movimiento:
CreaObjeto: Se supone que las variables del objeto ya están instanciadas con los valores del nuevo
objeto que se desea crear.
Public Sub CreaObjeto()
7
Dim Q As New rdoQuery
Dim stSQL As String
Set Q.ActiveConnection = Conex
stSQL = “INSERT INTO Movimiento (FechaHora, Cantidad,“
stSQL = stSQL & “BodegaCodigo, ArticuloCodigo) VALUES (“
stSQL = stSQL & “?,?,?,?)”
Q.SQL = stSQL
Q(0).Type = rdTypeTIMESTAMP
Q(0).Value = FechaHora
Q(1).Type = rdTypeNUMERIC
Q(1).Value = Cantidad
Q(2).Type = rdTypeNUMERIC
Q(2).Value = BodegaCodigo
Q(3).Type = rdTypeNUMERIC
Q(3).Value = ArticuloCodigo
Q.Execute
Set Q = Nothing
End Sub
DestruyeObjeto: Si la clase es una agregación de otras, se puede antes de eliminar el registro de la
base de datos, llamar al servicio DestruyeObjeto para cada uno de los objetos componentes. Esto
representa el hecho que al eliminar un todo, se eliminan también sus partes. Lo mismo se debe
realizar en herencias (al eliminar los padres).
Public Sub DestruyeObjeto()
Dim Q As New rdoQuery
Dim stSQL As String
Set Q.ActiveConnection = Conex
stSQL = “DELETE FROM Movimiento WHERE “
stSQL = stSQL & “ FechaHora = ?”
stSQL = stSQL & “ AND BodegaCodigo = ?”
stSQL = stSQL & “ AND ArticuloCodigo = ?”
Q.SQL = stSQL
Q(0).Type = rdTypeTIMESTAMP
Q(0).Value = FechaHora
Q(1).Type = rdTypeNUMERIC
Q(1).Value = BodegaCodigo
Q(2).Type = rdTypeNUMERIC
Q(2).Value = ArticuloCodigo
Q.Execute
Set Q = Nothing
End Sub
SalvaEstado: Se denomina el estado de un objeto a la tupla de valores de sus atributos en un cierto
momento. Al modificar un objeto se actualiza el registro en la base de datos con el estado o valores
de los atributos del objeto en memoria. Se actualizan sólo los campos que no son parte de la clave
primaria.
Public Sub SalvaEstado()
8
Dim Q As New rdoQuery
Dim stSQL As String
Set Q.ActiveConnection = Conex
stSQL = “UPDATE Movimiento
stSQL = stSQL = “ SET Cantidad = ? WHERE “
stSQL = stSQL & “ FechaHora = ?”
stSQL = stSQL & “ AND BodegaCodigo = ?”
stSQL = stSQL & “ AND ArticuloCodigo = ?”
Q.SQL = stSQL
Q(0).Type = rdTypeNUMERIC
Q(0).Value = Cantidad
Q(1).Type = rdTypeTIMESTAMP
Q(1).Value = FechaHora
Q(2).Type = rdTypeNUMERIC
Q(2).Value = BodegaCodigo
Q(3).Type = rdTypeNUMERIC
Q(3).Value = ArticuloCodigo
Q.Execute
Set Q = Nothing
End Sub
RecuperaEstado: Conociendo los atributos que forman la clave primaria (Oid u Object Id.) de un
objeto, es posible recuperar el valor del resto de ellos.
Public Sub RecuperaEstado()
Dim Q As New rdoQuery
Dim RS As rdoResultSet
Dim stSQL As String
Set Q.ActiveConnection = Conex
stSQL = “SELECT * FROM Movimiento
stSQL = stSQL & “ FechaHora = ?”
stSQL = stSQL & “ AND BodegaCodigo = ?”
stSQL = stSQL & “ AND ArticuloCodigo = ?”
Q.SQL = stSQL
Q(0).Type = rdTypeTIMESTAMP
Q(0).Value = FechaHora
Q(1).Type = rdTypeNUMERIC
Q(1).Value = BodegaCodigo
Q(2).Type = rdTypeNUMERIC
Q(2).Value = ArticuloCodigo
Set RS = Q.OpenResultSet
Me.LlenaDesdeResultSet RS
Set RS = Nothing
Set Q = Nothing
End Sub
Con la definición de estos cuatro servicios, la persistencia de los objetos se puede manejar desde las
aplicaciones (u otros servicios) sin necesidad de realizar consultas SQL.
9
Se desea que la capa intermedia represente fielmente al modelo de clases que define la estructura
del sistema. Por ello, para cada una de las relaciones de una clase, vamos a crear propiedades en
ella, que permitan a las aplicaciones o servicios de otras clases navegar por el modelo.
Las relaciones en un modelo de clases se definen con nombres de roles, dependiendo de la dirección
de la navegación. Si en el modelo no se han definido, podemos suponer que se utilizan los mismos
nombres de las clases, en singular o plural, dependiendo de la cardinalidad. Por ejemplo, desde
Bodega a Movimiento el nombre sería “Movimientos” y desde Movimiento a Bodega, “Bodega”.
Ocupando estos nombres de relaciones, escribiremos propiedades que permitan retornar él o los
objetos relacionados.
Para la clase Movimiento:
Public Property Get Bodega As Bodega
Dim Q As New rdoQuery
Dim RS As rdoResultSet
Dim ObjetoRelacionado As New Bodega
Set Q.ActiveConnection = Conex
Q.SQL = “SELECT * FROM Bodega WHERE Codigo = ?”
Q(0).Type = rdTypeNUMERIC
Q(0).Value = Me.BodegaCodigo
Set RS = Q.OpenResultSet
Set ObjetoRelacionado.Conex = Me.Conex
ObjetoRelacionado.LlenaDesdeResultSet RS
Set Bodega = ObjetoRelacionado
Set ObjetoRelacionado = Nothing
Set RS = Nothing
Set Q = Nothing
End Property
Para la clase Bodega:
Public Property Get Movimientos As Collection
Dim Col As New Collection
Dim Q As New rdoQuery
Dim RS As rdoResultSet
Dim ObjetoRelacionado As Movimiento
Q.SQL = “SELECT * FROM Movimiento WHERE BodegaCodigo = ?”
Q(0).Type = rdTypeNUMERIC
Q(0).Value = Me.BodegaCodigo
Set RS = Q.OpenResultSet
Do While Not RS.EOF
Set ObjetoRelacionado = New Movimiento
Set ObjetoRelacionado.Conex = Me.Conex
ObjetoRelacionado.LlenaDesdeResultSet RS
Col.Add ObjetoRelacionado
RS.MoveNext
Set ObjetoRelacionado = Nothing
Loop
Set RS = Nothing
Set Q = Nothing
End Property
10
Para el caso de especializaciones o herencias, se pueden utilizar como nombre para las propiedades:
“Padre” desde la clase hija a la padre, y el nombre de la clase hija en la dirección contraria. En este
último caso y en las asociaciones que aceptan cardinalidad cero, la propiedad debe ocuparse de que
los objetos relacionados no existan, pudiendo retornar objetos nulos (asignándoles la constante
Nothing).
Utilizando estas propiedades, se puede accesar en forma muy fácil a los objetos relacionados a
alguno en particular. Si se escribieran estas propiedades para todas las relaciones del modelo, las
navegaciones compuestas resultan en forma natural, como en el siguiente ejemplo:
Dim Bodega As New Bodega
Dim Conex As New rdoConnection
Conex.Connect = “dsn=Bodega;uid=sa;pwd=”
Conex. EstablishConnection rdDriverNoPrompt
Set Bodega.Conex = Conex
Bodega.Codigo = “B01”
Bodega.RecuperaEstado
MsgBox “La descripción de la bodega es “ & Boodega.Descripcion
Dim Mov As Movimiento
For Each Mov In Bodega.Movimientos
If Not Mov.Entrada Is Nothing Then
MsgBox “Entrada: ” & Mov.FechaHora
MsgBox “Articulo: “ & Mov.Articulo.Descripcion
Msgbox “Proveedor: “ & Mov.Entrada.Proveedor.Descripcion
Else
MsgBox “Salida: ” & Mov.FechaHora
MsgBox “Articulo: “ & Mov.Articulo.Descripcion
Msgbox “Destino: “ & Mov.Salida.Destinatario.Descripcion
End If
Next
Las propiedades que escribimos nos permiten consultar por objetos relacionados, pero hay
ocaciones en que es necesario asignar valores a estos objetos. Por ejemplo, antes de crear un
Movimiento, es necesario asignar la Bodega y El Artículo ya que (como es agregación) forman
parte de su Oid. Para ello, para las relaciones con origen en una clase (cardinalidad 0..* => 1)
creamos propiedades que permitan asignar el objeto relacionado. Lo mismo se debe realizar para
permitir la asignación del objeto padre cada vez que se crea un hijo.
En la clase Movimiento:
Public Property Set Bodega(Objeto As Bodega)
‘ Se asigna al atributo privado que mantiene la relación
BodegaCodigo = Objeto.Codigo
End Property
Public Property Set Articulo(Objeto As Articulo)
ArticuloCodigo = Objeto.Codigo
End Property
Entonces, antes de crear un nuevo movimiento, se asignan los objetos relacionados. Por ejemplo, si
queremos crear un nuevo movimiento de entrada para una bodega, artículo y proveedor ya
instanciados, podemos escribir:
11
Dim Mov As New Movimiento, Ent As New Entrada
Set Mov.Conex = Conex
Mov.Cantidad = 5
Set Mov.Bodega = BodegaInstanciada
Set Mov.Articulo = ArticuloInstanciado
Mov.CreaObjeto
Set Ent.Conex = Conex
Set Ent.Padre = Mov
Set Ent.Proveedor = ProveedorInstanciado
Ent.CreaObjeto
Set Mov = Nothing
Set Ent = Nothing
El lenguaje Visual Basic no soporta la herencia de objetos; sin embargo, ésta se puede simular
utilizando “funciones espejo.” Si tenemos una propiedad llamada “Padre” que retorne el objeto
asociado de la clase padre, podemos escribir en el hijo, propiedades que representes a los atributos,
servicios y relaciones de la clase padre. En el ejemplo anterior, para la clase Entrada:
Public Property Get FechaHora As Date
FechaHora = Padre.FechaHora
End Property
Public Property Get Cantidad As Long
Cantidad = Padre.Cantidad
End Property
Public Property Get Bodega As Bodega
‘ Se usa “set” porque en una referencia a un objeto
Set Bodega = Padre.Bodega
End Property
Public Property Get Articulo As Articulo
Set Articulo = Padre.Articulo
End Property
Si la clase padre a su vez heredara de otra más, sería necesario en la nieta declarar funciones espejo
para representar los atributos, relaciones y servicios de la clase abuela.
El resto de los servicios de las clases se escriben utilizando las propiedades y servicios de
persistencia que antes se definieron. En el ejemplo anterior, se necesita defnir los servicios de
Entrada y Salida de artículos para la clase Bodega.
Public Sub Entrada(Articulo As Articulo, Cantidad As Long, Proveedor
As Proveedor)
Dim Mov As New Movimiento
Dim Ent As New Entrada
Dim Sto As New Stock
‘ Crear objeto Movimiento en la bodega
Set Mov.Conex = Me.Conex
Set Mov.Bodega = Me
Set Mov.Articulo = Articulo
Mov.FechaHora = Now
Mov.Cantidad = Cantidad
Mov.CreaObjeto
12
‘ Crear el objeto Entrada como hijo de Mov
Set Ent.Conex = Me.Conex
Set Ent.Padre = Mov
Set Ent.Proveedor = Proveedor
Ent.CreaObjeto
‘ Crear o actualizar stock del artículo
‘ Buscar si existe. Si existe sumar a la cantidad
‘ sino crearlo.
Set Sto.Conex = Me.Conex
Set Sto.Articulo = Articulo
Set Sto.Bodega = Me
Sto.Cantidad = -99 ‘ si lo encuentra, cantidad va a cambiar
Sto.RecuperaEstado
If Sto.Cantidad = -99 Then ‘ No lo encontró
Sto.Cantidad = Cantidad
Sto.CreaObjeto
Else ‘ Lo encontró
Sto.Cantidad = Sto.Cantidad + Cantidad
Sto.SalvaEstado
End If
‘ Destruir variables
Set Sto = Nothing
Set Ent = Nothing
Set Mov = Nothing
End Sub
Public Sub Salida(Articulo As Articulo, Cantidad As Cantidad,
Destinatario As Destinatario)
Dim Sto As New Stock
Dim Mov As New Movimiento
Dim Sal As New Salida
‘ Buscar stock para ver si hay disponibilidad
Set Sto.Conex = Me.Conex
Set Sto.Bodega = Me
Set Sto.Articulo = Articulo
Sto.Cantidad = 0 ‘ Si no lo encuentra, queda en cero
Sto.RecuperaEstado
If Sto.Cantidad < Cantidad Then
Err.Raise vbObjectError, "Bodega", "No hay stock suficiente
para satisfacer la salida del artículo"
End If
Sto.Cantidad = Sto.Cantidad – Cantidad
Sto.SalvaEstado
‘ Crear objeto Movimiento en la bodega
Set Mov.Conex = Me.Conex
Set Mov.Bodega = Me
Set Mov.Articulo = Articulo
Mov.FechaHora = Now
Mov.Cantidad = Cantidad
Mov.CreaObjeto
‘ Crear el objeto Salida como hijo de Mov
Set Sal.Conex = Me.Conex
Set Sal.Padre = Mov
Set Sal.Destinatario = Destinatario
Sal.CreaObjeto
13
‘ Limpiar
Set Sal =
Set Sto =
Set Mov =
End Sub
variables
Nothing
Nothing
Nothing
Como se está construyendo bajo una arquitectura de tres capas, el servicio Salida no puede
desplegar un mensaje cuando la cantidad requerida es mayor que la disponible. La forma de avisar
los errores es empleando el estándar que provee ActiveX, a través del objeto Err. Este error puede
ser capturado (usando “On Error Goto”) desde la aplicación que invoca el servicio y es ella la
encargada de avisarlo al usuario.
Es conveniente crear algunos servicios especiales en las clases que faciliten la construcción de
interfaces. Por cada clase podemos crear propiedades (Get y Set) que permitan recuperar y asignar
la identificación de los objetos para poder referenciarlos desde las aplicaciones. Definamos
popiedades llamadas “Oid” para cada una de las clases. En el caso de Movimiento:
Public Property Get Oid() As Collection
Set Oid = New Collection
Oid.Add FechaHora
Oid.Add BodegaCodigo
Oid.Add ArticuloCodigo
End Property
Public Property Set Oid(Id As Collection)
FechaHora = Id(1)
BodegaCodigo = Id(2)
ArticuloCodigo = Id(3)
End Property
Una aplicación puede declarar una variable tipo colección u object para almacenar la clave de un
objeto y luego asignársela a éste de vuelta para recuperar su estado.
Otros servicios útiles para la construcción de interfaces serían aquellos que retornan colecciones de
objetos para mostrar en algún control. Debido nuevamente a la arquitectura de tres capas, no
deseamos que la aplicación accese a la base de datos, pero podemos construir servicios en la capa
intermedia que nos retornen grupos de objetos para mostrarlos en las ventanas, y permitir que el
usuario los seleccione.
Por ejemplo, podemos construir un servicio “Todas” en la clase bodega, de la forma:
Public Function Todas As Collection
Dim Q As New rdoQuery
Dim RS As rdoResultSet
Dim B As Bodega
Set Todas = New Collection
Set Q.ActiveConnection = Me.Conex
Q.SQL = “SELECT * FROM Bodega”
Set RS = Q.OpenResultSet
Do While Not RS.Eof
Set B = New Bodega
Set B.Conex = Me.Conex
B.LlenaDesdeResultSet RS
Todas.Add B
Set B = Nothing
Loop
Set RS = Nothing
14
Set Q = Nothing
End Function
15
Programación de la Capa de Aplicación.
La capa de aplicación corresponde a la interfaz con el usuario. Ella puede sólo accesar a objetos de
la capa del negocio y no directamente a la base de datos, por lo que no se debría escribir consultas
SQL o reglas del negocio dentro de los eventos de controles o elementos de interfaz.
Para la construcción de una aplicación, partimos con un nuevo proyecto en Visual Basic del tipo
“Standard EXE”. Dentro del ambiente integrado de programación, podemos tener abiertos más de
un proyecto a la vez, en un grupo de proyectos. Podemos agregar un proyecto nuevo “File|Add
project” al grupo y de esta forma tener en un mismo ambiente la capa intermedia (ActiveX dll) y la
aplicación (Standard EXE).
Seleccionando el proyecto de la aplicación agregamos una referencia hacia el proyecto de las clases
(Project|References). De esta forma, la capa intermedia constituye una biblioteca para la aplicación.
Desde el formulario principal de la aplicación podemos crear una conexión pública (un objeto
rdoConecction). Esta conexión se irá asignando a los objetos y otros formularios, a medida que se
vayan creando.
Mantención de objetos.
Una de las tareas comunes a todos los sistemas corresponde a la mantención de los datos que actúan
como “maestros”. En el ejemplo anterior, podemos realizar la mantención de las bodegas en el
siguiente formulario:
El control principal es un listbox desde donde el usuario
seleccionará un objeto. Podemos declarar una variable del
formulario, de la clase Bodega, que en todo momento esté
instanciada con la selección del usuario.
Al abrirse el formulario podemos llenar el listbox con el
resultado del servicio “Todas” de Bodega y actualizar la variable
del formulario cada vez que se produce un evento de cambio de
fila en el control.
Los botones de la parte superior pueden abrir otros formularion
que ejecuten los servicios CreaObjeto, DestruyeObjeto y EliminaObjeto que se definieron para cada
clase.
El código contenido en el formulario anterior es el siguiente:
Public Conex As rdoConnection
Private Bodega As Bodega
Private Todas As Collection
Private Sub cmdAgregar_Click()
Set frmAgregarBodega.Conex = Conex
frmAgregarBodega.Show 1
ActualizaListaBodegas
End Sub
Private Sub cmdEliminar_Click()
If MsgBox("¿ Confirma la eliminación de la bodega seleccionada ?",
vbYesNo) = vbYes Then
Bodega.DestruyeObjeto
ActualizaListaBodegas
16
End If
End Sub
Private Sub cmdModificar_Click()
Set frmModificarBodega.Conex = Conex
Set frmModificarBodega.Bodega = Bodega
frmModificarBodega.Show 1
ActualizaListaBodegas
End Sub
Private Sub Form_Load()
ActualizaListaBodegas
End Sub
Private Sub ActualizaListaBodegas()
Dim B As New Bodega
Set B.Conex = Conex
Set Todas = Bodega.Todas
Set B = Nothing
ListaBodegas.Clear
For Each B In Todas
ListaBodegas.AddItem B.Descripcion
Next
End Sub
Private Sub ListaBodegas_Click()
If Item >= 0 Then
Set Bodega = Todas(Item)
Else
Set Bodega = Nothing
End If
cmdModificar.Enabled = Not Bodega Is Nothing
cmdEliminar.Enabled = Not Bodega Is Nothing
End Sub
Los formularios para Agregar y Modificar una bodega tendrán el código necesario en su botón de
“Aceptar” para llamar a los servicios CreaObjeto y DestruyeObjeto, respectivamente.
Si un formulario necesita ejecutar servicios que como parámetros reciban otros objetos, éstos
últimos se pueden seleccionar desde listas o “ComboBox” como en el ejemplo anterior. Además de
los controles listbox, se pueden utilizar controles como el FlexGrid o el ListView que permiten
mostrar más columnas. Estas otras columnas pueden mostrar otros atributos del objeto o atributos
de objetos relacionados, usando las propiedades que se programaron en la capa intermedia.
Por ejemplo, si se muestran los movimientos a una bodega, las columnas se podrían llenar con el
siguiente código:
Set Movimientos = Bodega.Movimientos
For Each Mov In Movimientos
Fila=Fila+1
Grilla.String(Fila,1) = Movimiento.Fecha
Grilla.String(Fila,2) = Movimiento.Cantidad
Grilla.String(Fila,3) = Movimiento.Articulo.Descripcion
If Not Mov.Entrada Is Nothing Then
Grilla.String(Fila,4)=
Movimiento.Entrada.Proveedor.Descripcion
17
Else
Grilla.String(Fila,4)=
Movimiento.Salida.Destinatario.Descripcion
End If
Next
Utilización de la capa intermedia desde páginas web.
El servidor web de Microsoft, Internet Information Server (o IIS) soporta el uso de ASP (Active
Server Pages). Esto significa que las páginas pueden contener scripts que serán ejecutados en el
servidor, antes de retornar información al browser. Este código que ejecutará el servidor podrá
hacer uso de los servicios de la capa intermedia que definimos.
Existen algunas diferencias entre la programación de páginas ASP y aplicaciones Visual Basic
Cliente / Servidor. En ASP se cuenta con tres objetos principales que sirven para comunicar entre sí
las diferentes páginas que conforman un sitio.
El objeto Session permite declarar variables (que pueden ser objetos) que se conocen en todas las
páginas durante la conexión de un usuario. Acá se pueden, por ejemplo, guardar valores o
referencias a objetos que sirven de parámetros de entrada a cada página.
El objeto Request permite rescatar los valores de entrada de las páginas, a través del nombre que se
le dio en HTML a los campos (editores y listas).
Usando objeto Response se puede retornar código HTML hacia el browser.
Debido a que no se conoce la cantidad de usuarios que se conectarán a la base de datos y la cantidad
de conexiones son un recurso limitado, es más conveniente pasarle a cada clase la cadena de
conexión (y no el objeto conexión) para que ella se conecte a la base de datos antes de realizar
cualquier consulta. De esta forma, todas las clases podrían tener una variable pública llamada
“ConexString”, además de la conexión Conex y un servicio Conecta de la forma:
Private Sub Conecta
If Conex Is Nothing Then
Set Conex = New rdoConnection
Conex.Connect = ConexString
Conex.EstablishConnection rdDriverNoPrompt
End If
End Sub
Entonces, al inicio de cada servicio, las clases pueden siempre llamar a Conecta.
La sentencia New se reemplaza por la creación directa de los objetos, usando CreateObject. Por
ejemplo, si en el caso anterior llamamos al proyecto “ClasesBodegas” que contiene una clase
“Bodega”, la creación de un objeto y su conexión a la base de datos, sería de la forma:
Dim Bod
Set Bod = CreateObject(“ClasesBodegas.Bodega”)
Bod.ConexString = “dsn=Bodega;uid=sa;pwd=”
Response.Write Bod.StockComoHTML(Session(Articulo))
Set Bod = Nothing
18
Descargar