Como ya hemos visto en otras entradas, Xojo es un lenguaje de programación orientado a objetos (OOP), y entre otras cosas esto significa que soporta la sobrecarga de métodos. También hemos podido ver en otras entradas que algunos de estos métodos especiales son los Constructores de Clase, ¡pero hay muchos más! Por ejemplo, podemos sobrecargar los operadores, representados mediante los método encargados de realizar el sumatorio entre dos instancias de una misma clase, su resta, multiplicación o división. Pero también existe otro operador que podemos sobrecargar: Lookup. ¿Para qué sirve y qué ventajas nos proporciona?
Cuando definimos e implementamos el método Operator_Lookup en nuestras clases estaremos en disposición de utilizar la notación por punto para acceder o asignar al valor correspondiente al elemento indicado sin necesidad de que se encuentre previamente definido por la clase. Es decir, será responsabilidad de la implementación determinar cómo responder, y ya no del Compilador. Entre otras posibilidades, esto nos dará por ejemplo la flexibilidad de elaborar nuestra clase de preferencias completamente independiente de proyecto y totalmente multiplataforma, respaldando tanto las claves como los valores asociados a cada entrada de la preferencia mediante el uso de un Diccionario, mientras que para su acceso y asignación podremos utilizar la notación por punto de forma mucho más directa desde nuestro código. Veamos como podemos ponerlo en práctica.
El primer paso consiste evidentemente en añadir un nuevo elemento de Clase al proyecto sin asignar ninguna clase superior; es decir, se tratará de una clase raíz. En este tutorial le asignaremos el nombre MyPreferences
. A continuación añadiremos una propiedad con nombre Data
y de tipo Xojo.Core.Dictionary
, marcando su ámbito como Privado (lo que significa que sólo será accesible desde la propia clase). Con estos elementos ya tenemos establecido el medio que utilizaremos para el almacenamiento real de los pares claves y valores correspondientes a nuestra clase de Preferencias.
Ahora añadiremos un nuevo método Constructor encargado de inicializar el Diccionario que servirá como repositorio de los datos:
- Name: Constructor
- Scope: Public
E introduce la siguiente línea de código en el Editor de Código correspondiente al método recién creado:
Data = New Xojo.Core.Dictionary
Ahora añadiremos el método que realmente nos interesa, se trata de la sobrecarga de Operator_Lookup:
- Name: Operator_Lookup
- Parameters:
key as string, assigns value as auto
- Scope: Public
Y en el Editor de Código fuente resultante introduciremos la siguiente línea:
data.Value( key ) = value
¿Qué nos permite esto? Tal y como está, si añadimos en el evento Open del proyecto el siguiente código, podemos comprobar la ventaja que supone utilizar la notación por punto para utilizar miembros de la clase que realmente no están definidos en la misma, sino que son capturados por el método Operator_Lookup
, añadiendo el miembro como clave (es decir, el texto que incluyamos a continuación del punto), y la asignación establecida tras el igual como el valor para dicha clave; esto es lo que logramos utilizando la palabra clave Assigns
como parte de la definición del parámetro en la signatura del método:
Dim pr As New MyPreferences pr.page = 10 pr.index = "20" pr.baseColor = &c204060
Como puedes observar, Page
, Index
y BaseColor
no son miembros de nuestra clase sino que estos pasarán a ser capturados como “claves” para el Diccionario gracias a la sobrecarga del operador Operator_Lookup
, además de que también comprobamos como podemos asignar diferentes tipos de datos en la asignación de cada uno de ellos: un entero, un String y un Color.
Recuperar datos con Operator_Lookup
Ahora bien, ¿cómo podríamos recuperar los valores empleando igualmente la notación por punto? Evidentemente, tendremos que añadir un método adicional que vuelva a sobrecargar el operador Operator_Lookup
, salvo que en esta ocasión la signatura aceptará como entrada la clave que deseamos recuperar (el miembro indicado tras el punto), y devolveremos en todo caso un String para simplificar este ejemplo. Por tanto, la signatura del nuevo método que deberemos de añadir a la clase será la siguiente:
- Name: Operator_Lookup
- Parameters:
Key As String
- Return Type:
String
- Scope: Public
Y en el Editor de Código resultante introduciremos el siguiente código:
Dim a As Auto Dim s As String If data.HasKey(key) Then a = data.Value(key) Dim info As Xojo.Introspection.TypeInfo info = xojo.Introspection.GetType(a) If info = GetTypeInfo(Text) Or info = GetTypeInfo(String) Then s = a Elseif info = GetTypeInfo(Integer) Then s = CType(a, Integer).totext Elseif info= GetTypeInfo(Color) Then Dim c As Color = a s = Str(c) End If End If Return s
Como puedes comprobar, y tras comprobar si el Diccionario contiene la clave para la cual deseamos recuperar su valor, hacemos uso de los mecanismos de introspección para conocer cuál es el tipo asociado con la clave y en función del mismo utilizamos un sistema de conversión u otro para obtener el resultado deseado: un String; ya se trate de un entero, un texto/string o bien un color. Por supuesto, y para mantener la brevedad del ejemplo, no se contemplan otros tipos de datos como valores numéricos en coma flotante, booleanos, o incluso instancias de otras clases. Ahora bien, viendo sería sencillo de ampliar para contemplar otras opciones.
Si ahora volvemos al evento Open del proyecto y completamos el código de la siguiente forma:
Dim pr As New MyPreferences pr.page = 10 pr.index = "20" pr.baseColor = &c204060 MsgBox pr.page + EndOfLine + pr.index + EndOfLine + pr.baseColor
Observaremos como no sólo podemos asignar los valores sino también recuperarlos como String, tal y como nos habíamos propuesto.
Consideraciones finales
Ahora bien, a la hora de utilizar esta práctica hemos de tener en cuenta algunas cuestiones. Por ejemplo, es evidente que no podremos hacer uso del completado automático de código puesto que Xojo desconoce por completo el nombre de los miembros que escribimos. También tenemos que tener especial cuidado en asegurarnos de que escribimos exactamente el mismo miembro tanto en la asignación como en la recuperación del valor, puesto que tampoco contaremos con las comprobaciones realizadas por el compilador.
Por último, sería bastante fácil ampliar la clase de ejemplo para contemplar otros tipos de datos no incluidos inicialmente, así como añadir un método adicional de serialiado que permitiese obtener todos los pares clave/valor para su almacenamiento sobre soporte físico, así como un constructor que nos permita inicializar una instancia de clase con los valores almacenados sobre un soporte físico. Igualmente, dado que es bastante probable que sólo quieras mantener una instancia de clase de preferencias en tu aplicación, también podrías implementarlo como un Singleton. ¿Te animas?