Programación Orientada a Objetos I

Ver el tema anterior Ver el tema siguiente Ir abajo

Programación Orientada a Objetos I

Mensaje por JoseRios el Mar Feb 26, 2008 1:13 pm

Introducción

El desarrollo orientado a objetos (POO) no tiene que ser misterioso. Es realmente nada más que otra forma de organizar tu código. Las buenas noticias es que si estas escribiendo aplicaciones Notes/Domino en LotusScript, ya sabes como usar objetos porque las clases front-end y back-end como NotesUiDocument y NotesDatabase son fundamentales en todo lo que haces. Sin embargo, eres como la mayoría de los desarrolladores de Notes/Domino que conozco, escribes tu código para usar esos objetos en las subrutinas predefinidas ( como QueryOpen y QuerySave ) que Domino Designer crea para tus elementos de diseño, y además escribes tus propias subrutinas y funciones. Eso parece ser típico, incluso entre los más avanzados desarrolladores de LotusScript, pero hay un montón de cosas que puedes hacer con la Orientación a Objetos para mejorar tus aplicaciones.
Definiciones básicas

Antes de empezar, miremos unas pocas definiciones. Un objeto es algo con un nombre, un conjunto de atributos, y un conjunto de métodos. Los atributos son valores de datos, y los métodos son piezas de código que pueden manipular los valores de datos. Una clase es un grupo de dimensiones, subrutinas y funciones que pueden representar y manipular los atributos de un objeto. Los dimensionamientos configuran el almacenaje de los atributos del objeto, y las subrutinas y funciones contienen el código de los métodos del objeto. Las clases tambien pueden contener un tipo especial de rutina, llamada propiedad, que es utilizada para obtener o fijar un dato que fue "dimensionado" usando la palabra clave Private.

Los datos, propiedades y métodos, y métodos de un objeto pueden sólo ser referenciados a traves de la "notación punto" usando la instancia a la izquierda del punto, y el dato, propiedad, o método a la derecha. Por ejemplo, si tienes una clase llamada Book con una propiedad llamada Title, puedes tener un código como este :


Código:
Dim ThingOne as New BookDim ThinbTwo as New Book
ThingOne.Title = "The Cat In The Hat"
ThingTwo.Title = "Green Eggs And Ham"

A propósito, un avispado programador una vez observó que la programación orientada a objetos sonaba un poco menos solemne y complicada si tu sustituías la palabra cosa por la palabra objeto dondequiera que la vieses. Ahora, echemos una mirada a algo de código orientado a cosas.

Creando una clase simple en LotusScript

Primero echaremos una mirada a una clase simple en LotusScript. En si misma, no es muy útil. Es simplemente un contenedor para una cadena y un valor clave para identificarse. Por favor, confia en mi. En conjunción con algunas otras clases que nosotros vamos a construir, pienso que verás que será un bloque construido muy manejable.

Por ejemplo, la clase podría tener una propiedad adicional que devolviera una dimensión métrica incluso aunque el dato del elemento interno esté almacenado en el sistema inglés. Además, al utilizar una propiedad Set para cada dato, puedes protegerla de ser alterada por cualquier usuario de esa clase debido a que el dato en cuestión es privado. No obtendremos esas ventajas si permitimos el acceso directo a los datos de una clase. En el mundo de la Orientación a Objetos, el proveer acceso a los datos de esta forma se llama Ocultación de datos. Aunque es posible declarar elementos de datos en una clase como publicos; en general , los elementos de datos de una clase deberían ser privados. Los usuarios de la clase deberán estar provistos de propiedades, funciones y subrutinas para acceder sólo a la información que ellos necesitan. Esto les prevendrá de cambiar los datos de una clase de formas no apropiadas.

Código:
Public Class ListItem  Private m_key As String
  Private m_value As String
  Sub new (key  As String, value As String)
      If key = "" Then ´ don't allow a blank key
        Error E_BLANK_KEY , E_BLANK_KEY_MSG
      End If
      m_key = key
      m_value = value
  End Sub
  Property Get key As String
      Key = m_key
  End Property
  Property Set key As String
      If key = "" Then ´ don't allow a blank key
        Error E_BLANK_KEY , E_BLANK_KEY_MSG
      End If
      m_key = key
  End Property
  Property Get value As String
      value = m_value
  End Property
  Property Set value As String
      m_value = value
  End Property

  Sub PrintItem
      Print "ListItem: key = " & Me.m_key & " value = " &
      Me.m_value
  End sub
End Class

Si miras el método New y la propiedad Set Key, verás que ambas emiten un error cuando encuentran una clave en blanco. ¿Porqué es eso? Simplemente porque esa es la mejor manera disponible para indicar que ha ocurrido un problema. Ni la propiedad ni el método tienen un valor de retorno disponible para señalizar condiciones de error.

Creando una clase usando la composición

¿Qué podemos hacer con la clase ListItem? No mucho sin más clases. Creamos otra clase para que podamos hacer algo util. El tipo de datos list de LotusScript es una herramienta extremadamente util. Si has hecho mucho uso de ella, habrás notado que no hay forma de obtener una cuenta de los elementos en una lista sin llevar la cuenta tu mismo ( o recorrer la lista ). Aquí tenemos una clase que puede usarse como un "interfaz" sobre una lista, que solventa ese problema. La clase BetterList BarraLateral contiene una versión no interrumpida del código ).

Código:
Public Class BetterList
  Private m_list List As Variant
  Private m_count As Integer
  Property Get Count As Integer
      Count = m_count
  End Property
  Public Function DeleteList
      Erase m_list
  End Function
  Public Sub new
      m_count = 0
  End Sub
  Sub Delete
      Call Me.DeleteList
  End Sub

Nota que las variables de clase empiezan todas con la cadena m_. Esto es una convención que copié del mundo C++. Nos permite saber de un vistazo qué variables pertenecen a la clase de las que son de la función actual. He encontrado que esta convención nos puede ahorrar un montón de tiempo, especialmente en clases grandes. Nota tambien que m_list es privada; no hay forma de que alguien usando esta clase trastee con la lista directamente. Ese es el único camino para estar absolutamente seguro que el contador permanecerá correcto.

A continuación están las distintas funciones de la clase BetterList ( o métodos ). Son como las funciones regulares, pero están definidas dentro de la clase.

Código:
Public Function DeleteItem( key As String) As Integer
  Dim rval As Integer
  if ( Iselement(m_list(key)) ) Then
      Erase m_list(key)
      m_count = m_count - 1
      rval = True
  Else
      rval = False
      Print "Item " & key & " not found. It could not be deleted."
  End If
  DeleteItem = rval
End Function
Public Function AddItem(key As String, item As ListItem) As Integer
  If ( Not Iselement(m_list(key)) ) Then
      Set m_list(key) = item
      m_count = m_count + 1
  Else
      Erase m_list(key)
      Set m_list(key) = item
  End If
End Function

En la siguiente sección de codigo, verás que GetItem devuelve un variant. ¿Porqué es asi? Bien, la función podría retornar un ListItem, pero entonces tendriamos que tener clases adicionales como BetterList que devolvieran cada tipo de objeto con el que nosotros quisieramos tratar. De esta forma una clase puede manejar tipos múltiples de objetos. Usando variants de esta forma podemos causar problemas si lo hacemos sin cuidado. Si GetItem devuelve un elemento del tipo equivocado, obtendremos un error en tiempo de ejecución cuando intentemos tratarlo como el tipo que esperamos.

En la siguiente clase ,EnhancedUIDoc, vemos una forma de evitar este problema. Alli, inmediatamente asignamos la variable variant a un objeto del tipo correcto y cualquier otro posterior acceso es hecho via el objeto. Esta asignación en EnhancedUIDoc tambien tiene el efecto de revelar errores en los nombres de propiedad y funciones en tiempo de compilación en vez de en tiempo de ejecución. Prefiero usar el compilador para encontrar errores siempre que pueda. Es bastante más facil que encontrar problemas a traves del testeo y ahorra tiempo tambien.

Esta es la función más importante en esta clase. Es utilizada para tener acceso a los objetos que estamos guardando en la lista.

Tambien, justo debajo, puedes ver que hemos puesto código que maneje errores. Este indica el tipo de error y la clase y método ( o propiedad ) donde ha ocurrido el error; Esto entonces permite al proceso continuar. Aunque básico, esto puede ayudar bastante en la búsqueda del origen de un error. Sin ella, tendremos que hacer un montón de tediosa depuración antes incluso de que sepamos en que parte del codigo el problema ha ocurrido. Si tu aplicación es compleja y haces uso de clases de forma extensiva, encontrarás que esta práctica te puede ahorrar un montón de tiempo. Tambien, puedes querer el considerar crear una base de datos específicamente para grabar los errores.

Código:
Public Function GetItem(key As String) As Variant
  Dim itm As ListItem
  On Error ErrListItemDoesNotExist Goto NoSuchItem
  Set GetItem = m_list(key)
Ok:
  Exit Function
NoSuchItem:
  Print "List item " & key & " not found."
  Set itm = Nothing
  Set GetItem = itm
  Resume OK
End Function

Public Function IsInList(key As String) As Integer
  Dim rval As Integer
  If ( Iselement(m_list(key)) ) Then
      rval = True
  Else
      rval = False
  End If
  IsInList = rval
End Function

End Class

Las funciones y subrutinas en una clase ( por ejemplo, GetItem o AddItem como están usadas aquí ) son frecuentemente llamadas métodos en el mundo de la orientación a objetos. La subrutina Sub New en una clase tiene un nombre especial; es llamada el constructor. Es llamada automáticamente cuando un objeto de esa clase se crea. Esta subrutina es usada comunmente para hacer cualquier inicialización y trabajo de configuración necesitado por la clase.

Es tambien posible tener otra subrutina especial, la Sub Delete. Es conocida como el destructor. Se llama automáticamente cuando un objeto de esa clase es borrado. Ninguna de estas subrutinas especiales es requerida. El constructor es tan útil que raramente no se declara. No trataremos con un destructor a menos que existan características de gestión de memoria con las que tratar.

La teoría de la composición

Por cierto, el término técnico para construir una clase de esta forma es composición - tambien conocido como agregación. En este caso, hemos construido una clase nueva que contiene varios tipos de datos definidos previamente. Las clases pueden tambien contener datos que son objetos en sí mismos. Veremos cómo funciona eso en breve.

En el mundo de la orientación a objetos, la composición o agregación es llamada relación "Tiene un". Un coche tiene un motor y ruedas, la fruta tiene semillas y piel, y así. Muchos libros que explican lenguajes orientados a objetos usan diagramas y código basados en ejemplos como ese. Estos diagramas ayudan ciertamente a ilustrar los conceptos de la POO y lo de abajo es mi propia versión, aunque no estoy tan seguro sobre el código que tradicionalmente va con los diagramas. Funcionan, pero no hacen nada util. Pienso que el código incluido en este artículo es un argumento mejor para usar las técnicas de Orientación a Objetos. Muestran como hacer algo util de una manera orientada a objetos.
Construyendo clases por composición



Crear una clase extendiendo una clase predefinida

La clase BetterList es util tal cual; hace cualquier cosa que una lista estandar de Notes puede hacer. No vamos a parar aquí. Usando las dos clases que acabamos de construir, construiremos una nueva clase que puede ser usada para añadir un conjunto de características comunes a todos los formularios de una base de datos. Incluso seremos capaces de cambiar esas características donde las estándares no se deseen. Ya que no es posible crear nuevas clases que hereden de las clases predefinidas de Notes, vamos a extender las capacidades de la clase de front-end NotesUIDocument usando la técnica de la composición de nuevo. Llamaremos a la nueva clase EnhancedUIDoc.

Por el hecho de simplificar, esta clase asume que todos los valores de los campos serán cadenas y que habrá solo un valor por campo. Sería fácil cambiar esta clase para que tratara con diferentes tipos de campo y valores múltiples. Los tipos de campo pueden ser determinados a traves de la propiedad Type de NotesItem. La propiedad NotesItem.Values devuelve una matriz que puede ser contada para determinar si contiene un valor único o múltiples valores.

Aquí está la clase EnhancedUIDoc, la cual contiene a la clase BetterList. ( Para una versión no interrumpida del código, vea la clase EnhancedUIDoc de la barra lateral ).

Código:
Class EnhancedUIDoc
  Private m_uidoc As NotesUIDocument
  Private m_uiw As NotesUIWorkspace
  Private m_origvalues As BetterList
  Private m_doctype As String
  Sub ProcessPostOpen( Source As NotesUIDocument )
      Dim doc As NotesDocument
      Dim Itm As ListItem
      Print("EnhancedUIDoc - ProcessPostopen")
      Set doc = m_uidoc.document
      Forall i In doc.Items
        Set Itm = New ListItem( i.Name, i.Values(0) )
        Call m_origvalues.AddItem(i.Name, Itm)
      End Forall
  End Sub
  Sub ProcessQuerySave( Source As Notesuidocument, Continue As Variant )
      Dim doc As NotesDocument
      Dim Itm As ListItem
      Dim rval As Integer
      Dim v As Variant
      Print( " EnhancedUIDoc - ProcessQuerysave")
      rval = continue
      Set doc = m_uidoc.document
      Forall i In doc.Items
        Set v = m_origvalues.GetItem(i.Name)

Aquí esdonde nosotros devolvemos el variant al objeto. Debemos asegurarnos de que no es nulo primero. De otra forma puede que no sea un objeto real.

Código:
        If (Not Isnull(v) ) Then
            Set Itm = v
            If i.Values(0) <> Itm.value Then
              Print "Item " & i.Name & " new value = " & i.Values(0)
              rval = True
            Else
              Print "Item " & i.Name & " not changed."
            End If
        Else
            Print "Item " & i.Name & " not found."
        End If
      End Forall
    Continue = rval
  End Sub 
  Sub new( uid As NotesUIDocument )
      Print ("EnhancedUIDoc - sub new")
      Set m_uiw = g_wks
      Set m_origvalues = New BetterList
      Set m_uidoc = uid

En las siguientes lineas, echa una mirada a las sentencias On Event. ¿Que va a ir aqui? Reemplazamos algunas subrutinas genéricas manejadoras de eventos con unas nuevas definidas dentro de nuestra clase. Esta es una manera más en la que podemos consolidar nuestro código cuando usamos clases. El código en ProcessQuerysave y ProcessPostopen podría haber sido puesto en las subrutinas Querysave y Postopen del formulario, pero eso significaría separar el código para estos eventos del código del resto de la clase.

Código:
      On Event Querysave From m_uidoc Call ProcessQuerysave
      On Event Postopen From m_uidoc Call ProcessPostopen
  End Sub
  Sub Postopen( Source As Notesuidocument)
  End Sub
  Sub Querysave(Source As Notesuidocument, Continue As Variant)
  End Sub
End Class

Aunque esta clase se creó usando la composición, tambien forma la base para nuestro ejemplo de herencia porque las dos siguientes clases que crearemos heredarán de ella.

Segunda Parte -->
avatar
JoseRios

Cantidad de envíos : 39
Edad : 32
Localización : Santiago, Chile
Fecha de inscripción : 25/02/2008

Ver perfil de usuario http://noteroschile.wordpress.com

Volver arriba Ir abajo

Ver el tema anterior Ver el tema siguiente Volver arriba

- Temas similares

 
Permisos de este foro:
No puedes responder a temas en este foro.