Programación Orientada a Objetos I
Página 1 de 1.
Programación Orientada a Objetos I
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 :
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.
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 ).
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.
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.
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 ).
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.
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.
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 -->
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 -->
Página 1 de 1.
Permisos de este foro:
No puedes responder a temas en este foro.
|
|