Artículo
· 9 hr atrás Lectura de 7 min

¿Method o Class Method?

Para los programadores nuevos en ObjectScript, inevitablemente surgirá una pregunta: “¿Cuál es la diferencia entre Methods y ClassMethods?” Una respuesta típica sería: “Un ClassMethod se aplica a una clase, pero un method se aplica a una instancia de esa clase.” Aunque esa respuesta es correcta, carece de información importante sobre cómo estos métodos difieren y cómo se usan en ObjectScript. Muchas cosas podrían escribirse como cualquiera de los dos. Por ejemplo, supongamos que tenemos una clase llamada “User.Person” con una propiedad llamada “Name”. Si quisiéramos crear un método para escribir ese nombre, podríamos hacer cualquiera de las siguientes cosas:

Method WriteName()
{
    write ..Name
}
ClassMethod ClassWriteName(id)
{
    write ##class(User.Person).%OpenId(id).Name
}

En muchos casos, no importa cuál elijamos. Sin embargo, hay algunas razones convincentes por las que podríamos preferir uno sobre el otro. Hoy profundizaremos en este tema para descubrirlo.

Methods: líneas generales

Los methods a veces se denominan métodos de instancia (instance Methods) para mayor claridad. Se seleccionan cuando el método hace algo con, o a, una instancia específica (objeto). Los métodos de instancia (Method) son las herramientas a usar cuando se requiere una instancia en memoria de un objeto. Esto, por supuesto, significa que solo deben definirse en clases de objetos. Dado que operan sobre una instancia de esa clase (objeto), pueden referirse a las propiedades de ese objeto utilizando la sintaxis de doble punto, como en el método WriteName() mencionado anteriormente. Uno de los ejemplos más básicos con los que todos nos familiarizamos rápidamente es %Save(). Si no tenemos una instancia del objeto, es imposible saber qué guardar.

Los métodos de instancia también son una excelente opción cuando un método debe poder operar sobre una instancia de un objeto antes de que esa instancia sea guardada (o insertada a través de SQL si la clase extiende %Persistent). %ValidateObject() es un gran ejemplo de esta situación. Necesitamos asegurarnos de que el objeto sea válido antes de guardarlo.

 

Class Methods: líneas generales

Aunque los métodos de clase (ClassMethods) se definen dentro de una clase, no están ligados a una instancia específica de una clase. Si estáis familiarizados con algunos otros lenguajes de programación, puede que estéis acostumbrados a llamar a estos métodos "static methods". Dado que estos métodos no se llaman en una instancia específica de la clase, no podéis emplear la sintaxis de doble punto para referiros a las propiedades de la clase. En su lugar, debéis obtener una instancia de un objeto y luego usar la sintaxis de punto para referiros a las propiedades de esa instancia.

Los classmethods deben usarse cuando un método no tiene acceso a una instancia. Los métodos que crean una nueva instancia de un objeto, también conocidos como "constructores", en la mayoría de los casos, serán métodos de clase. Una excepción situacional a esto es %ConstructClone(). Este construye una nueva instancia de un objeto copiada de una instancia existente, por lo que se define como un método de instancia (Method). El constructor predeterminado para la mayoría de los objetos es %New(), pero también podéis escribir algunos propios.

Los métodos de clase son de elección cuando un método opera sobre múltiples instancias de una clase. Podríamos transformar estos métodos en métodos de instancia y hacer que funcionen perfectamente, pero hacerlo no tendría mucho sentido. Por ejemplo, si quisiéramos convertir todos nuestros nombres a mayúsculas, podríamos usar el siguiente método:

 

ClassMethod AllToUpper(){
    set stmt = ##class(%SQL.Statement).%New()
    set query = "SELECT ID FROM SQLUser.Person"
    do stmt.%Prepare(query)
    set result = stmt.%Execute()
    while result.%Next(){
        set myPerson = ##class(User.Person).%OpenId(result.GetData(1))
        set myPerson.Name = $ZCONVERT(myPerson.Name,"U")
        do myperson.%Save()
    }
}

Si definiéramos este método como un método de instancia (Method), aún haría lo mismo. Sin embargo, normalmente no lo hacemos porque requeriría que tomáramos el paso adicional de crear o abrir una instancia de la clase antes de llamar al método, lo cual sería una pérdida de tiempo y recursos.

Del mismo modo, se eligen los métodos de clase (ClassMethod) cuando un método no opera sobre instancias de un objeto. Supongamos que tenemos un parámetro de clase definido, en este caso, para sobrescribir el parámetro de título, haríamos lo siguiente:

 

/// Optional name used by the <i>Form Wizard</i> for a class when generating forms.
Parameter CAPTION = "People";

Dado que los parámetros se comparten entre todos los miembros de una clase (una “variable estática” como se llama en algunos otros lenguajes de programación), no es necesario acceder a una instancia específica de una clase para referirse a ese parámetro. Si quisiéramos escribir el valor de ese parámetro, podríamos hacerlo como se muestra a continuación

ClassMethod WriteCaption()
{
    write ..#CAPTION
}

Una vez más, en este caso, podríamos convertirlo en un método de instancia. Sin embargo, sería tan ineficiente como en nuestro ejemplo anterior.

ClassMethods: casos especiales

Hasta ahora, todas estas han sido pautas generales de programación, que, por cierto, podríais romper y aún así obtener el resultado deseado. Sin embargo, hay algunos casos específicos en los que debéis usar un ClassMethod.

Si queréis llamar a un método en un trigger, debe ser un ClassMethod. Los triggers pueden ser poderosos, pero también tienen algunas reglas adicionales y una sintaxis especial, ya que se ejecutan cuando los datos están en movimiento. Permiten especificar si deseáis referiros al valor antiguo o al nuevo valor de una propiedad que está cambiando, y esa es precisamente la razón por la que no pueden usar la sintaxis de doble punto. Supongamos que quiero registrar la marca de tiempo UTC de un registro que se está eliminando de mi Person. Podría definir el siguiente método y trigger para lograrlo como se indica a continuación:

 

ClassMethod LogDelete(id)
{
    set ^LogDelete("Person",id) = $ZDATETIME($ZTIMESTAMP)
}

Trigger LogDelete [ Event = DELETE, Foreach = row/object ]
{
    do ##class(User.Person).LogDelete({%%ID})
}

Entonces, cuando elimine un registro de esa tabla, veré un conjunto global como el siguiente:

Si queremos que un método sea expuesto como un procedimiento SQL, debe ser un ClassMethod. Si mi campo de nombre es simplemente un primer y segundo nombre separados por un espacio, “David Hockenbroch”, pero queremos tener un procedimiento almacenado que lo devuelva de la manera preferida por Elon Musk para su pedido telefónico, “Hockenbroch, David”, podemos escribir una función simple como la que se muestra a continuación:

ClassMethod AlphaName(id) As %String [ SqlProc ]
{
    set myPerson = ##class(User.Person).%OpenId(id)
    return $P(myPerson.Name," ",2)_", "_$P(myPerson.Name," ",1)
}

Fijaos en la palabra clave SqlProc en la definición de la función. Esta palabra clave indicará al compilador que proyecte este método como un procedimiento SQL llamado Person_AlphaName. Luego, podemos explotarlo en una consulta de la siguiente manera:

select name, person_alphaname(id) As "AlphaName" from person

Veremos el siguiente resultado:

Finalmente, puedo utilizar un class method para definir cómo se calcula una propiedad computada en SQL. Supongamos que tengo la siguiente propiedad en mi clase:

Property AlphaName As %String [ SqlComputed ];

Cuando utilizamos la palabra clave SqlComputed, debemos definir cómo se realizará el cálculo. Una forma de hacerlo es con la palabra clave SqlComputeCode. La otra opción es especificar un class method que devuelva el valor deseado. Este método debe tener el mismo nombre que su propiedad, seguido de “Computation”. Considerad el siguiente ejemplo:

ClassMethod AlphaNameComputation(cols As %PropertyHelper) As %String
{
    return $P(cols.getfield("Name")," ",2)_", "_$P(cols.getfield("Name")," ",1)
}

Si hacemos eso, la propiedad se calculará cada vez que el objeto sea insertado o actualizado. Fijaos en el argumento %PropertyHelper que se pasa a este método. Este objeto permite acceder a las propiedades de la instancia individual cuya propiedad está siendo calculada, a pesar de que sea un ClassMethod. Podéis usar la propiedad getfield de ese objeto y proporcionar un nombre de propiedad para obtener esa propiedad. En este caso, solo tenemos una columna, por lo que deberíamos usar el SQL.

select Name, AlphaName from person

El resultado que obtendréis entonces será idéntico al anterior:

En conclusión

Espero que esta revisión de los métodos haya sido lo suficientemente detallada e incluya todo lo que necesitáis saber para determinar qué tipo de método usar. Si he omitido algo, ¡por favor, hacédmelo saber en los comentarios a continuación!

Comentarios (0)1
Inicie sesión o regístrese para continuar