Resumen Minicurso de programación en tres capas   (5 mensajes )

Mensaje enviado por "Diego Palmeira" <dpalmeira@internetcreativa.com.ar>

Business Objects (Parte 3)

Hola amigos Listeros =) !!!

En esta entrega, continuando con el articulo de Business Objects,vamos a adentrarnos con el codigo de la primera clase llamada "User".

---------------------------------------------------------------------------------------
Nota:
---------------------------------------------------------------------------------------
En cada nueva entrega voy a asumir que ya leyeron los articulos anteriores, puesto que asi, no vuelvo a escribir lo mismo cada vez.

Recuerden que no deben copiar este codigo, ya que les estare enviando el proyecto completo, para que lo puedan probar. En el veran muchas mas cosas, pero no quiero que se distraigan con el codigo "de relleno", simplemente deseo que se centralizen en lo mas importante que por ahora son las clases.
Despues veran que varias constantes y otras cosas como las clases de apertura de base de datos, son lo mismo para cualquier otro proyecto, por eso es importante que presten especial atencion a lo que veremos en cada etapa. No se adelanten preguntandome cosas que todavia no explique, ya que mi tiempo es escaso y no puedo responder mails y ponerme a explicar las cosas varias veces.

Dicho esto..... ADELANTE ! VAMOS A ARMAR NUESTRO PRIMER COM !
---------------------------------------------------------------------------------------
Fin Nota
---------------------------------------------------------------------------------------

El ejemplo que les estoy entregando esta compuesto de 2 proyectos.
Uno dedicado a nuestro COM (Activex DLL), y el otro a un pequeño consumidor de nuestro COM, el cual es un ejemplo, de como realizaria un ABM de usuarios (Alta Baja y Modificacion=ABM), contenido en un Exe estandard de VB6.

Recuerden que despues enviare, como, con este mismo COM, hacer un ABM de usuarios en ASP.

---------------------------------------------------------------------------------------
Clase User (Contenida en nuestro proyecto AppObjects Activex DLL)
El codigo lo voy a ir comentando para que vayan entendiendo un poco mas.

La clase User posee Metodos, Propiedades y un Evento.

En principio el Objeto User, controla su edicion. Es decir controla las reglas de
negocio que deben cumplirse, para que el mismo este en condiciones de ser grabado en la
base de datos. Esto asegura que si la capa de nuestro COM o Business Object, no valida
los datos de un objeto en particular, este no puede ser salvado en el DB. Mas alla que
el DB tambien controle la integridad de los datos.

Para lograr que cada propiedad, como por ejemplo el Nombre, sea correcto, el objeto
mantiene una coleccion de Reglas Rotas, en la cual a medida que el mismo es editado
(es decir alguien cambia algo a traves de las properties LET o SET), el va metiendo
en esa coleccion que propiedades se encuentran rotas. Cuando uno setea por ejemplo el
nombre y este ya no es una regla rota, la misma es eliminada de la coleccion de reglas
rotas. Cuando la Coleccion de reglas rotas (BrokenRules), se encuantra vacia, el objeto
User lanza un evento a quien lo instancio (por ahora la interfaz VB), para indicarle
que el objeto ya no posee reglas rotas y de esta forma se encuentra en un estado valido
para ser salvado en el DB.

La coleccion de reglas rotas, es como si fuera un Array que contiene que propiedades
no cumplen los requerimientos, o las reglas de negocio establecidas.
La misma esta implementada a traves de una clase llamada "BrokenRules", que por ahora
no interesa como funciona, simplemente pensemos que es un contenedor de las reglas
rotas.

---------------------------------------------------------------------------------------

Option Explicit

' El siguiente evento, es disparado cada vez que el objeto cambia de estado, con reglas
' rotas, o sin reglas rotas. En el caso de nuestra interfaz VB, esta capturara el evento
' para saber si debe o no habilitar lo botones de "OK" y "Apply".
' Es super importante ver en este punto el encapsulamiento, ya que es el propio objeto
' el que regula su propio estado, no debo preocuparme luego en la Interfaz de controlar
' el mismo, ya que es una tarea del objeto en si.
' De esta forma estamos asegurando que las N interfaces (o cualquier otra cosa) que use
' el COM, seran validadas en el momento de salvar el mismo, y nunca apareceran datos
' incorrectos en el DB.

' Nota: en ASP no puedo capturar los eventos, ya que el objeto User no lo puedo crear
' WithEvents por ser un ambiente "Desconectado", es decir cada vez que una pagina es
' enviada al lado cliente, todos los estados de nuestros objetos, ya estan muertos,
' porque el propio objeto fue destruido.
Event Valid(IsValid As Boolean) ' evento para las Reglas Rotas.

' Estas son variables de uso interno que se usan a modo de flag para controlar la
' edicion del mismo.
Private blnNew As Boolean
Private blnDeleted As Boolean
Private blnDirty As Boolean
Private blnEditing As Boolean

' Declaracion de la coleccion que controlara las reglas rotas.
Private WithEvents objValid As BrokenRules

' Propiedades de nuestro objeto.
Private Type Properties
   'Hacer: Declarar Propiedades.
   UserID As Long
   UserName As String * 20
   Password As String * 20
   FirstName As String * 50
   LastName As String * 50
   Telephone As String * 100
   eMail As String * 100
   Enabled As Boolean
   AvailableUserName As Boolean
End Type

' Lo que sigue son las UDT que poseen las propiedades de nuestro objeto.
' Una contiene las propiedades actuales, y la otra el estado anterior a la edicion,para
' de esta forma restorear el estado del objeto si este es cancelado despues de comenzada la
' edicion. Es decir, alguien cambia una propiedad, ejemplo el nombre, pero luego presiona
' el boton cancel.
Private udtProps As Properties
Private udtSave As Properties

'Hacer: Reglas de Negocios a controlar.
Private Const kRULE_ERROR_USERNAME As String = "Debe indicar el Nombre de Usuario o Login (hasta 20 caracteres)"
Private Const kRULE_ERROR_PASSWORD As String = "Debe indicar la Contraseña del Usuario (hasta 20 caracteres)"
Private Const kRULE_ERROR_FIRSTNAME As String = "Debe indicar el Nombre del Usuario (hasta 50 caracteres)"
Private Const kRULE_ERROR_LASTNAME As String = "Debe indicar el Apellido del Usuario (hasta 50 caracteres)"
Private Const kRULE_ERROR_EMAIL As String = "Debe indicar la dirección de eMail del Usuario (hasta 100 caracteres)"

'Hacer: Personalizar Constantes.
Private Const kFILE As String = "User" ' Este es el nombre del archivo de nuestra clase.
Private Const kTABLE As String = "Users" ' Esta es la tabla de la DB.

Private Sub Class_Initialize()
    ' Inicializacion del objeto, se ejecuta cuando alguien crea un nuevo objeto de nuestra clase.

    Set objValid = New BrokenRules
    blnNew = True

    'Hacer: Inicializar propiedades String.
    udtProps.UserName = ""
    udtProps.Password = ""
    udtProps.FirstName = ""
    udtProps.LastName = ""
    udtProps.Telephone = ""
    udtProps.eMail = ""

    RefreshRules ' Refresca Reglas Rotas.
End Sub

'Hacer: Implementar Control de Reglas Rotas.
Private Sub RefreshRules()
    objValid.RuleBroken kRULE_ERROR_USERNAME, Rule_ErrorUserName()
    objValid.RuleBroken kRULE_ERROR_PASSWORD, Rule_ErrorPassword()
    objValid.RuleBroken kRULE_ERROR_FIRSTNAME, Rule_ErrorFirstName()
    objValid.RuleBroken kRULE_ERROR_LASTNAME, Rule_ErrorLastName()
    objValid.RuleBroken kRULE_ERROR_EMAIL, Rule_ErrorEmail()
End Sub

' En cada una de estas funciones se controlan las reglas de negocio.
' Ejemplo: que el nombre no sea cadena vacia.

Private Function Rule_ErrorUserName() As Boolean
    Rule_ErrorUserName = Not (Len(Trim$(udtProps.UserName)) > 0)
End Function

Private Function Rule_ErrorPassword() As Boolean
    Rule_ErrorPassword = Not (Len(Trim$(udtProps.Password)) > 0)
End Function

Private Function Rule_ErrorFirstName() As Boolean
    Rule_ErrorFirstName = Not (Len(Trim$(udtProps.FirstName)) > 0)
End Function

Private Function Rule_ErrorLastName() As Boolean
    Rule_ErrorLastName = Not (Len(Trim$(udtProps.LastName)) > 0)
End Function

Private Function Rule_ErrorEmail() As Boolean
    Rule_ErrorEmail = Not (Len(Trim$(udtProps.eMail)) > 0)
End Function

' Esta property devuelve separado por ";" las reglas que el objeto tiene rotas
' en un determinado momento de su edicion.
Public Property Get ShowBrokenRules() As String
    ShowBrokenRules = objValid.ShowElements
End Property

' Metodo que pone al objeto en modo de edicion, puede ser un objeto nuevo, o uno
' que fue cargado de la base de datos para editarse.
Public Sub BeginEdit()
   ' Si el objeto ya se esta editando no es posible correr de nuevo este metodo en
   ' este mismo obj, hasta que se Aplique o Cancele su edicion.

   If blnEditing Then Err.Raise kERROR_ACTION_NOT_SUPPORT, kFILE & "::BeginEdit", _
                      "El Objeto ya se está editando"

   ' Al comenzar la edicion, salvo el estado de las properties, por si se calcela la misma.
   LSet udtSave = udtProps
   blnEditing = True
End Sub

' Metodo que cancela la edicion. Si no se estaba editando lanza un error a las capas superiores.
Public Sub CancelEdit()
   If Not blnEditing Then Err.Raise kERROR_ACTION_NOT_SUPPORT, kFILE & "::CancelEdit", _
                          "El Objeto no se está editanto"
   blnEditing = False
   blnDeleted = False
   ' restore object state
   LSet udtProps = udtSave ' Restablece el estado del objeto en caso de ser cancelado.
End Sub

' Este metodo Aplica la Edicion, es decir o lo salva o lo borra de la base de datos, siempre
' y cuando el objeto no posea Reglas Rotas.
Public Sub ApplyEdit()
On Error GoTo ErrorHandler
    If Not blnEditing Then Err.Raise kERROR_ACTION_NOT_SUPPORT, kFILE & "::ApplyEdit", _
                            "El Objeto no se está editando"

    'Inicio Transacción.
    objData.db.BeginTrans

    ' Alta o Modificación del Objeto.
    If (blnDirty Or blnNew) And Not blnDeleted Then
        If Not IsValid Then Err.Raise kERROR_ACTION_NOT_SUPPORT, kFILE & "::ApplyEdit", _
                             "El Objeto no se encuentra en un estado válido"
        Save ' aqui realmente se salva el objeto en la DB.
        LSet udtSave = udtProps
        blnNew = False
    End If
    'Baja del Objeto.
    If blnDeleted And Not blnNew Then
        DeleteObject
        blnNew = True
        blnDeleted = False
    End If
    blnDirty = False
    blnEditing = False

    objData.db.CommitTrans

Exit Sub
ErrorHandler:
    objData.db.RollbackTrans ' Si ocurre un error, cancela la transaccion en el DB.
    Err.Raise Err.Number, Err.Source, Err.Description
End Sub

' Propiedad que devuelve si posee reglas rotas.
Public Property Get IsValid() As Boolean
   IsValid = (objValid.Count = 0)
End Property

' Eventos que lanza el objeto que controla las reglas rotas, y que este objeto (user), sigue
' pasando a las capas superiores.
Private Sub objValid_BrokenRule()
   RaiseEvent Valid(False)
End Sub

Private Sub objValid_NoBrokenRules()
   RaiseEvent Valid(True)
End Sub

' Deletea el objeto, pero todavia no lo borra de la base de datos, esto simplemente
' pone una marca como de borrado, pero hasta que no se llame al metodo ApplyEdit(), el
' objeto no sera eliminado del DB.
Public Sub Delete()
Dim strReason As String
    If Not blnEditing Then Err.Raise kERROR_ACTION_NOT_SUPPORT, kFILE & "::Delete", _
                            "El Objeto no se está editando"
    If Not EnableToDelete(strReason) Then _
        Err.Raise 512 + vbObjectError + 1002, kFILE & "::Delete", strReason
    blnDeleted = True
End Sub

Private Function EnableToDelete(ByRef pStrReason As String) As Boolean
Dim blnEnabled As Boolean
    pStrReason = ""
    blnEnabled = True ' En esta linea en vez de devolver true, se podria controlar alguna condicion
         ' necesaria para poder realizarse el borrado.
    EnableToDelete = blnEnabled
End Function

' Propiedad que devuelve si el obj, esta en estado borrado.
Public Property Get IsDeleted() As Boolean
   IsDeleted = blnDeleted
End Property

' Propiedad que devuelve si el obj, es nuevo. (es decir nunca fue grabado en el DB, todavia esta solo en RAM)
Public Property Get IsNew() As Boolean
    IsNew = blnNew
End Property

' Propiedad que devuelve si el obj, esta sucio, es decir si alguna de sus propiedades
' fue cambiada y necesita ser salvado para mantener los cambios persistentes.
Public Property Get IsDirty() As Boolean
    IsDirty = blnDirty
End Property

' Propiedades que corresponden exclusivamente a nuestro objeto.
' Recordar que las properties Get son de lectura desde el exterior,
' y las Let o Set son para meter un valor en nuestra propiedad.
' Estas se ejecutan al hacer por ejemplo desde el exterior:
' objUser.UserName = "Pepe" , es este caso el igual dispara la property LET
' de la propiedad UserName
' MsgBox objUser.UserID , en este caso se ejecuta la property GET.

' Si no estoy editando el objeto, en ninguna property LET, puedo asignar un valor,
' por eso se lanza un error si esto sucede.

Public Property Get UserID() As Long
    UserID = udtProps.UserID
End Property

Public Property Get UserName() As String
    UserName = Trim$(udtProps.UserName)
End Property

Public Property Let UserName(xValue As String)
    If Not blnEditing Then Err.Raise 383, kFILE & "::LetUserName", _
                           "El Objeto no se está editando"
    udtProps.UserName = xValue
    objValid.RuleBroken kRULE_ERROR_USERNAME, Rule_ErrorUserName()
    blnDirty = True ' Indica que una propiedad fue cambiada.
End Property

Public Property Get Password() As String
    Password = Trim$(udtProps.Password)
End Property

Public Property Let Password(xValue As String)
    If Not blnEditing Then Err.Raise 383, kFILE & "::LetPassword", _
                           "El Objeto no se está editando"
    udtProps.Password = xValue
    objValid.RuleBroken kRULE_ERROR_PASSWORD, Rule_ErrorPassword()
    blnDirty = True
End Property

Public Property Get FirstName() As String
    FirstName = Trim$(udtProps.FirstName)
End Property

Public Property Let FirstName(xValue As String)
    If Not blnEditing Then Err.Raise 383, kFILE & "::LetFirstName", _
                           "El Objeto no se está editando"
    udtProps.FirstName = xValue
    objValid.RuleBroken kRULE_ERROR_FIRSTNAME, Rule_ErrorFirstName()
    blnDirty = True
End Property

Public Property Get LastName() As String
    LastName = Trim$(udtProps.LastName)
End Property

Public Property Let LastName(xValue As String)
    If Not blnEditing Then Err.Raise 383, kFILE & "::LetLastName", _
                           "El Objeto no se está editando"
    udtProps.LastName = xValue
    objValid.RuleBroken kRULE_ERROR_LASTNAME, Rule_ErrorLastName()
    blnDirty = True
End Property

Public Property Get Telephone() As String
    Telephone = Trim$(udtProps.Telephone)
End Property

Public Property Let Telephone(xValue As String)
    If Not blnEditing Then Err.Raise 383, kFILE & "::LetTelephone", _
                           "El Objeto no se está editando"
    udtProps.Telephone = xValue
    blnDirty = True
End Property

Public Property Get eMail() As String
    eMail = Trim$(udtProps.eMail)
End Property

Public Property Let eMail(xValue As String)
    If Not blnEditing Then Err.Raise 383, kFILE & "::LeteMail", _
                           "El Objeto no se está editando"
    udtProps.eMail = xValue
    objValid.RuleBroken kRULE_ERROR_EMAIL, Rule_ErrorEmail()
    blnDirty = True
End Property

Public Property Get Enabled() As Boolean
    Enabled = udtProps.Enabled
End Property

Public Property Let Enabled(xValue As Boolean)
    If Not blnEditing Then Err.Raise 383, kFILE & "::LetEnabled", _
                           "El Objeto no se está editando"
    udtProps.Enabled = xValue
    blnDirty = True
End Property

Public Property Get AvailableUserName() As Boolean
    AvailableUserName = udtProps.AvailableUserName
End Property

' Metodo que realiza la carga del objeto. En este caso el mismo puede ser cargado por su ID
' o por el UserName. Generalmente se carga por el ID y es un parametro obligatorio en el metodo
' Load, pero en este caso en particular necesitamos cargar tambien por el UserName, por ejemplo
' si un usuario se esta logueando a nuestra aplicacion, y debemos verificar el Login.
' Ya que en este caso solo tenemos el UserName que la persona ingreso.
Public Sub Load(Optional ByVal xlngID As Long = 0, Optional ByVal xUserName As String = "")
    If blnEditing Then Err.Raise kERROR_ACTION_NOT_SUPPORT, kFILE & "::Load", _
                       "El Objeto se está editanto"
    If Not blnNew Then Err.Raise kERROR_ACTION_NOT_SUPPORT, kFILE & "::Load", _
                       "El Objeto No es Nuevo"
    Fetch xlngID, xUserName
    blnNew = False
    blnDirty = False
    RefreshRules ' Refresca Reglas Rotas.
End Sub

' ---------------------- Esta es la parte de nuestro objeto que accede al DB.(son todos metodos privados de la DLL)

' Borra fiscicamente el objeto del DB.
Private Sub DeleteObject()
Dim lngRecordsAffected As Long
    objData.db.Execute "DELETE FROM " & kTABLE & " " & _
                       "WHERE UserID = " & CStr(udtProps.UserID), lngRecordsAffected
    If lngRecordsAffected <> 1 Then
        ' Informar que o no se pudo dar de baja ó se dio de baja mas de uno.
        ' Ejemplo informar al usuario o almcenarlo en un LOG.
    End If
End Sub

' La funcion del Fetch es la encargada de traer 1 obj del DB y volcar los datos en las propiedades.
Private Sub Fetch(ByVal xlngID As Long, ByVal xUserName As String)
Dim oRs As Recordset
Dim strSQL As String

    If xUserName <> "" Then
       strSQL = "SELECT * FROM " & kTABLE & " WHERE UserName=" & "'" & xUserName & "'"
    Else
       strSQL = "SELECT * FROM " & kTABLE & " WHERE UserID=" & CStr(xlngID)
    End If
    Set oRs = New Recordset
    oRs.Open strSQL, objData.db, adOpenForwardOnly, adLockReadOnly
    If Not oRs.EOF Then
        With oRs
            'Hacer: Volcar Todas las Propiedades en la UDT.
            udtProps.UserID = Val(!UserID)
            udtProps.UserName = !UserName & ""
            udtProps.Password = !Password & "" ' aqui se podria des-encriptar la password.
            udtProps.FirstName = !FirstName & ""
            udtProps.LastName = !LastName & ""
            udtProps.Telephone = !Telephone & ""
            udtProps.eMail = !eMail & ""
            udtProps.Enabled = !Enabled
            udtProps.AvailableUserName = False
        End With
    Else
        Err.Raise 512 + vbObjectError + 1004, kFILE & "::Fetch", _
                  "No se encontró la fila en la Tabla: " & kTABLE & " ID: " & xlngID
    End If
    oRs.Close
    Set oRs = Nothing
End Sub

Private Sub Save()
Dim oRs As Recordset
Dim strSQL As String

   Set oRs = New Recordset

   ' Verifica si el UserName ya existe.
   strSQL = "SELECT UserID FROM " & kTABLE & " WHERE UserName LIKE '" & _
             Format(udtProps.UserName, ">") & "'" & _
             " AND UserID<>" & udtProps.UserID
   oRs.Open strSQL, objData.db, adOpenForwardOnly, adLockReadOnly

   If oRs.EOF Then
      oRs.Close
      If blnNew Then
          strSQL = kTABLE
          oRs.CursorLocation = adUseClient
          oRs.Open strSQL, objData.db, adOpenStatic, adLockPessimistic
          oRs.AddNew
      Else
          strSQL = "SELECT * FROM " & kTABLE & " " & _
                   "WHERE UserID=" & udtProps.UserID
          oRs.Open strSQL, objData.db, adOpenForwardOnly, adLockPessimistic
      End If
      With oRs
          !UserName = udtProps.UserName
          !Password = udtProps.Password ' Aqui se podria encriptar la Password antes de salvarla en el DB.
          !FirstName = udtProps.FirstName
          !LastName = udtProps.LastName
          !Telephone = udtProps.Telephone
          !eMail = udtProps.eMail
          !Enabled = udtProps.Enabled
          If blnNew Then
            .Bookmark = .Bookmark ' Truco para que refresque el ID Autonumérico en el RS. (sino quedaria en 0)
            udtProps.UserID = !UserID
          End If
          .Update
          .Close
      End With
      udtProps.AvailableUserName = True
   Else
      udtProps.AvailableUserName = False
   End If
   Set oRs = Nothing
End Sub

Private Sub Class_Terminate()
   Set objValid = Nothing
End Sub
---------------------------------------------------------------------------------------
Fin clase User
---------------------------------------------------------------------------------------

Notar que la propiedad "AvailableUserName", solo indica si fue posible dar de alta el UserName, ya que solo sabemos si existe en el momento de grabar el objeto, y no queremos que este se repita porque sera el nombre de usuario que usaran las personas para loguearse, ejemplo si quisieramos hacer un login en nuestra aplicacion interfaz. (en la UI ASP lo haremos asi)
Esta clase es la principal dentro de nuestro COM, y es la encargada de manejar todos los usuarios que se creen. Por eso es importante entender como funciona.

Mi consejo es que descompriman los proyectos del Zip que les envio en un directorio llamado "AppExample".
Luego abran el grupo de proyectos AppGroup.vbg, ejecuten el proyecto AppExample y vean como funciona el ABM de Usuarios, y vayan viendo como este interactua con el COM y la clase User.

Luego en los siguientes articulos nos encargaremos de la Coleccion Users y el proyecto VB EXE Interfaz (AppExample) en si. Por ultimo veremos las clases anexas del COM, como por ejemplo BrokenRules, cDataBaseAccess, y OpenConn.

---------------------------------------------------------------------------------------
Bueno, espero que se entienda, como veran es medio dificil explicar esto, pero creo
que si van mirando el codigo, les quedara mas claro. Igualmente con los siguientes
articulos, vamos a ir en detalle a todos los puntos, para que no quede nada sin explicar.

Me despido hasta la proxima.
y les mando un saludo a todos desde mi dulce hogar en Argentina !

---------------------------------------------------------------------------------------

Diego Palmeira Buenos Aires - Argentina
dpalmeira@internetcreativa.com.ar
(5411) 4774-5315 de 9 a 18 Hs.

Mensaje enviado por "Diego Palmeira" <dpalmeira@internetcreativa.com.ar>

Bueno yo pienso ir volcando de a poquito, a modo de ejemplo, la metodologia de objetos que yo utilizo y me dio resultado, y ademas lo voy a explicar con mis palabras y es como lo he explicado a muchos amigos para que lo entiendan, tratare de ser lo mas didactico posible.

Cual es la Idea ?
Simple, armar y conocer un modelo de objetos que sirve tanto para aplicaciones VB, como para ambiente WEB.

El primer paso es entender que tendemos a construir nuestras aplicaciones de modo que sean escalables, mantenibles y reutilizando la mayor parte de codigo posible.
Justamente esto conicide con el concepto de componentizar las cosas, para tener estos beneficios.
Hay un dicho que dice: "cuantas mas lineas de código tiene mi app, mayor tiempo me pasaré manteniendo el mismo en el futuro", obviamente el tiempo es dinero.

Vamos a construir una DLL (Activex DLL), con unas clases simples, para poder mostar un ejemplo de como hacer un ABM de usuarios, con Interfaz VB y WEB.

Fin Parte I

Diego Palmeira (desde Palermo - Buenos Aires Argentina)

Mensaje enviado por "Diego Palmeira" <dpalmeira@internetcreativa.com.ar>

 No, el ejemplo es sin MTS, igualmente si incluye transaccion a nivel DB, para resguardar la integridad de los mismos.
El tema del MTS es que para utilizar toda la funcionabilidad, necesitas que el objeto no tenga estado, es decir para ademas de transaccional, sea reutilizable o pooleable.
El modelo de Rocky usa estado para el objeto, es decir que mantiene en ram el estado hasta que lo destruis, por eso no soporta el Pooling de objetos del MTS.

Pero igualmente si mantenes la transaccion a nivel DB, no vas a tener problema, con eso, es decir va a funcionar bien, y yo vi aplicaciones grandes con cientos de clases usando Rocky de esta forma, sin ningun problema, de que queden cosas mal grabadas, igualmente acordate que la base de datos tiene que tener las respectivas relaciones que le dan un nivel mas de proteccion.

Un Objeto sin estado significa que solo tiene metodos como si fueran funciones, que al invocarlas les debo pasar todo lo necesario para realizar su trabajo, porque no puede mantener el estado en RAM.

En realidad, para empezar vamos a ver el ejemplo mas sencillo, despues avanzaremos a las 3 capas reales, con el MTS y lo de tener los objetos en distintas maquinas. Igualmente para la mayor parte de las aplicaciones normales mas pequeñas, con lo que veremos en los primeros ejemplos, tenes de sobra, es decir te sirve para WEB y VB y lo mas importante, te vas a dar cuenta que empezar a programar una aplicacion, sin usar OBJETOS, tiene sus desventajas !
Bueno espero haber aclarado tu duda.
bye

---- Mensaje original ------ Tu ejemplo es a 3 capas, con MTS ? Si es con
MTS es verdad que no deben o no debiera usarse metodos y/o propiedades en
los objetos ?

Mensaje enviado por "Diego Palmeira" <dpalmeira@internetcreativa.com.ar>

Business Objects (Parte 2)

Bueno en esta entrega vamos a comenzar con los pasos necesarios para armar nuestro primer COM con la metodologia de Rocky LHotka, o mas bien su modelo de objetos, que
en realidad voy a explicarlo sobre la version mia, que fue modificada, para soportar mejor la parte WEB, y posee algunas mejoras en cuanto al codigo, para que sea facil armar clases rápidamente.


El ejemplo estará compuesto de estas 3 capas:

1) Interfaz de Usuario VB (EXE)
2) Objetos de Negocios VB (Activex DLL COM)
3) Base de Datos (MS Access) (fácilmente migrable a SQL Server)

1) La interfaz de usuario interectuara con el COM de modo que ella no conozca en lo absoluto donde esta la Base de Datos, ni tampoco las tablas ni campos, ni siquiera
tendra referencia a ADO. De esta forma separo la parte de Datos de la Interfaz, encargandole todo el trabajo a nuestro COM, logrando asi un encapsulamiento y reutilizacion de esta capa, (por ejemplo para reutilizarla desde ASP), ademas de mojora de la performance y prolijidad del Codigo.

Esta interfaz se ocupará de instanciar los objetos que requiera (en nuestro caso solo para mostrar un ABM de usuarios), y recibirá los eventos que las distintas clases le envien, para poder enterarse de que esta pasando con el Componente.

2) Nuestras clases de ejemplo se alojaran en un Activex DLL, y cumplirán el siguiente ROL. Los objetos serán capaces de grabarse, cargarse, y controlar su edición.

3) Por último, la DB será en donde nuestros objetos serán persistentes, es decir donde se guardarán durante su ciclo de vida.

--------------------------------------------------------------------------------------

Nota: para los que no entienden que es una clase
Imaginen que tienen una maquina que hace botellas de plástico utilizando una matríz
de metal, la cual es llenada de plastico caliente, para generar las botellas.

La matríz, sería la clase, que define las propiedades y metodos de la botella,
y cada una de las botellas generadas (instanciadas), serian el equivalente a los
objetos.
Las propiedades serian por ejemplo: Color, Capacidad (litros), alto, etc. Los Metodos, serian por ejemplo: Como llenarla, como abrirla, etc.

Este ejemplo es bastante sencillo de entender. A partir de la matriz se generan
los objetos que utilizaré, y su aspecto y comportamiento, corresponde a como es la Matriz, es decir la Clase.
--------------------------------------------------------------------------------------

Lo primero que hay que indentificar, es cuales van a ser nuestras clases.

En el modelo definiré 2 clases pricipales,
- User y
- Users

La Clase User generará nuestro objeto User, el cual utilizaremos en la interfaz para el ABM de usuarios.

La clase Users, es una colección de objetos User, es decir que contendrá los objetos Users que se quieran cargar, dentro.

La clase User, será persistente, es decir que los objetos que genere a partir de esta, se almacenaran en la Base de datos, mediante metodos definidos para tal fin.

Para lograr esto, en la base de datos solo existe una tabla llamada Users, con los siguientes campos:

UserID (Autonumérico) Clave Principal
UserName Texto(20) requerido
Password Texto(20) requerido
FirstName Texto(50) requerido
LastName Texto(50) requerido
Telephone Texto(100) No-requerido
eMail Texto(100) requerido
Enabled Sí/No requerido

La clase User básicamente contará con los siguientes métodos Públicos principales:

Metodos:

   Load
   BeginEdit
   CancelEdit
   Delete
   ApplyEdit

Estos metodos son los encargados de operar el objeto, tanto para cargar uno, editarlo,
borrarlo, aplicar la edicion, o cancelar la misma.

Propiedades:

   UserID As Long
   UserName As String * 20
   Password As String * 20
   FirstName As String * 50
   LastName As String * 50
   Telephone As String * 100
   eMail As String * 100
   Enabled As Boolean
   AvailableUserName As Boolean ' Esta ultima es para uso interno de la clase, no posee persistencia.

Eventos:

 Valid

El evento Valid, es capturado por quien creo el objeto (usando WithEvents), ejemplo un form de la aplicacion interfaz, para indicarle a las capas superiores si el objeto posee, o no, "reglas de negocio rotas".
Es decir si todas sus propiedades como por ejemplo el Nombre, estan bien ingresados,
y de esta forma, por ejemplo un Form podria habilitar los botones de OK y Apply,
para que el usuario pueda confirmar el objeto que esta editando, ejemplo un usuario (objeto User).

De esta forma el propio objeto es capaz de llevar un control para validarse a si mismo
y asegurarse que cueando es salvado, tenga todos los datos correctos.

Notar que al meter las validaciones en el propio objeto, estoy encapsulando esta
funcionabilidad que es del propio objeto, y asegurando que desde cualquier interfaz que desee utilizarlo, nunca van a aparecer objetos mal grabados con campos vacios en la
base de datos. Esto esta muy bueno, si pensamos en que la unica forma de acceder a la
tabla User es a través del objeto User. ;)


Los "Metodos" y las "Propiedades", definen la interfaz que un objeto expone al mundo
exterior, es decir es la unica forma de utilizar el mismo. Por eso es importante
al definir un Sub o Function, prestar especial atencion a si es Private o Public,
ya que podria por error dejar un metodo que yo no quiero en forma publica, y que
un programador de interface, pueda utilizar por error.


Como se estaran dando cuenta, esta forma de trabajar es muy buena, ya que permite
que los developers, cumplan distintos roles en un desarrollo.

Es decir, a mi por ejemplo me encanta mucho mas programar solo Componentes, que
despues otros developers utilizan. Gracias a la encapsulacion y aislamiento del codigo, podemos lograr que por ejemplo el software se produzca tipo fabrica.

Es decir, que haya un grupo que se dedica solo a Interfaz utilizando una biblioteca
publicada en la LAN con Componentes que otro Team crea, los cuales solo desarrolan
componentes para los demas programadores.

Y por otro lado Administradores de Base de Datos que solo escriben los procedimientos almacenados (Stored Procedures = SP) y diseñan los DB.


Por ahora no se hagan problema si no entienden mucho, porque al ir viendo el código verán que es bastante entendible, lo que pasa que al princio, para los
que venian programando linealmente, es decir proceduralmente, sin utilizar clases
es dificil, lo se. Yo mismo lo pase despues de migrarme de DOS Clipper y Assembler al ambiente VB.

Es mas facil que alguien entienda objetos, si nunca programo proceduralmente, los conceptos de las programaciones OOP y orientada a eventos, no es que son mas compleja, sino son "DIFERENTES"

Bueno me tengo que ir despidiendo de este articulo "Business Objects Parte 2", ya que es la 1:20 AM, y tengo que dormir porque sino mañana no me voy a levantar para ir a trabajar y poder mandar esto a la lista.

Bueno, como veran mi tiempo es caotico, por eso tengan pasciencia para la proxima entrega, y denme feedback si esta bien explicado asi, o estoy tomando mucho vino
(o mejor TEQUILA MEXICANO Jejejee).

PD: Ya estuve armando un ejemplo, para que todo esto que veamos aca, lo puedan
probar con un ejemplo mio, ya escrito y funcionando. Tengo que hacer las ASP, asi
tienen todo el ejemplo completo, utilizar el ABM desde interfaz Win32 o WEB.


Bueno bye =)

--------------------------------------------------------------------------------------

Diego Palmeira Buenos Aires - Argentina
dpalmeira@internetcreativa.com.ar

Mensaje enviado por "Diego Palmeira" <dpalmeira@internetcreativa.com.ar>

En realidad, ya estas mirando cosas mas complejas GOOD ! el tema es asi.

Si pasas un objeto por parametro entre distintas maquinas (espacio de direcciones diferentes obviamente), eso produce toda una rosca de Marshaling o algo asi, y eso
mata tu aplicacion, porque requiere muchos recursos para poder emular del lado cliente
el objeto pasado o referenciado.

Es por eso que Lhotka serializa el estado del objeto y lo manda como un string todo junto, y del otro lado lo vuelve a armar, eso lo hace para evitar eso, pero hay muchos
debates sobre este tema que estara resulto en .Net segun adelanto Rocky, para
resolverlo de otra forma mas limpia. Quiza utilizando XML.

La cosa es que el serializa el estado del objeto, lo envia del otro lado, y lo reconstruye, sin dejarle eso a DCOM, directamente busco su propia solucion, ya que un
string es algo que pasas facilmente y por valor.

Bueno espero haber aclarado un poquito.

bye

-----Mensaje original-----
De: Juan Carlos Alvarez [mailto:jcarlos.alvarez@abitab.com.uy]
Enviado el: Viernes, 02 de Noviembre de 2001 10:59 a.m.
Para: visualbasic-esp@yahoogroups.com
Asunto: RE: (VB-ESP) Business Objects (Parte 2)


Diego, me parece muy interesante tu aporte. Muchas gracias.

Estuve adelantando un poco a lo que va a ser tu ejemplo, y entendiendo lo
que hace Lhotka.
Yo habia hecho cosas que me parecian en tres capas (al menos separo bien con
objetos lo que es la interfaz, el dominio y la base de datos en un mismo
proyecto VB). Y esto de separar en dll, veo que es mucho mejor...

Tengo una pregunta, para voz o para el que sepa:

Digamos que tenemos una clase Cliente en el dominio (TaskObjects) y otra
clase ClientePersistente en la capa de base de datos (TaskServer).

Lo que vi en el modelo de Lhotka es que, por ejemplo, un metodo Save de la
clase ClientePersistente tiene como parametros strings que indican
informacion de mi objeto Cliente por medio de su UDT.

Mi pregunta es: ¿ Puedo en vez de tener parametros strings en mi clase
ClientePersistente, tener directamente un objeto Cliente como parámetro ?

Se que si esto anda bien la gran desventaja es que tengo que tener en mi
proyecto TaskServer una referencia a TaskObjects, y por consiguiente mi capa
de base de datos pasa a depender de mi capa de negocios.

Gracias Juan.



Resumen Resumen

Visual Basic Página de Visual Basic

Página principal Página principal

www.jrubi.com