Saltar al contenido principal
Versión: Siguiente

Clases

Generalidades​

El lenguaje 4D soporta el concepto de clases. En un lenguaje de programación, el uso de una clase permite definir el comportamiento de un objeto con propiedades y funciones asociadas.

Una vez definida una clase usuario, puede instanciar los objetos de esta clase en cualquier parte de su código. Una vez definida una clase usuario, puede instanciar los objetos de esta clase en cualquier parte de su código. Una clase puede extend otra clase, y luego hereda sus funciones y propiedades (declaradas y calculadas).

El modelo de clases en 4D es similar al de las clases en JavaScript, y se basa en una cadena de prototipos.

Por ejemplo, puede crear una clase Person con la siguiente definición:

//Class: Person.4dm
Class constructor($firstname : Text; $lastname : Text)
This.firstName:=$firstname
This.lastName:=$lastname

Function get fullName() -> $fullName : Text
$fullName:=This.firstName+" "+This.lastName

Function sayHello() -> $welcome : Text

$welcome:="Hello "+This.fullName

En un método, creando una "Persona":

var $person : cs.Person //object of Person class
var $hello : Text
$person:=cs.Person.new("John";"Doe")
// $person:{firstName: "John"; lastName: "Doe"; fullName: "John Doe"}
$hello:=$person.sayHello() //"Hello John Doe"

Los archivos de clase se gestionan a través del Explorador 4D (ver Crear clases).

Borrar una clase​

Para eliminar una clase existente, selecciónela en el Explorador y haga clic en o elija Mover a la Papelera en el menú contextual.

También puede eliminar el archivo de clase .4dm de la carpeta "Classes" de su disco.

Class stores​

Las clases disponibles son accesibles desde sus class stores. Hay dos class stores disponibles:

  • cs para las clases de usuario y las class stores de los componentes
  • 4D para las clases integradas

cs​

cs : Object

ParámetrosTipoDescripción
classStore4D.ClassStore←Class store usuario para el proyecto o componente

El comando cs devuelve el almacén de clases de usuario para el proyecto o componente actual. Por defecto, sólo las clases ORDA están disponibles. Por defecto, sólo las clases ORDA están disponibles.

Ejemplo​

Quiere crear una nueva instancia de un objeto de myClass:

$instance:=cs.myClass.new()

4D​

4D : Object

ParámetrosTipoDescripción
classStore4D.ClassStore←Class store 4D

El comando 4D devuelve el almacén de clases para las clases 4D integradas. Ofrece acceso a las APIs específicas como CryptoKey.

Ejemplos​

Quiere crear una nueva llave en la clase CryptoKey:

$key:=4D.CryptoKey.new(New object("type";"ECDSA";"curve";"prime256v1"))

Quiere listar las clases integradas en 4D:

var $keys : Collection
$keys:=OB Keys(4D)
ALERT("There are "+String($keys.length)+" built-in classes.")

El objeto clase​

Una clase es un objeto de la clase "Class". Cuando una clase es definida en el proyecto, se carga en el entorno del lenguaje 4D. Un objeto clase tiene las siguientes propiedades y funciones:

Además, un objeto clase puede hacer referencia a un objeto constructor (opcional).

Un objeto de clase en sí mismo es un objeto compartido y, por tanto, se puede acceder a él desde diferentes procesos de 4D simultáneamente.

Herencia​

Si una clase hereda de otra clase (es decir, se utiliza la palabra clave Class extends en su definición), la clase padre es su superclass.

Cuando 4D no encuentra una función o una propiedad en una clase, la busca en su superclass; si no la encuentra, 4D sigue buscando en la superclase de la superclase, y así sucesivamente hasta que no haya más superclase (todos los objetos heredan de la superclase "Object").

Palabras clave de clase​

En las definiciones de clase se pueden utilizar palabras claves específicas de 4D:

  • Function <Name> para definir las funciones de clase de los objetos.
  • Class constructor para inicializar nuevos objetos de la clase.
  • property para definir propiedades estáticas de los objetos con un tipo.
  • Function get <Name> y Function set <Name> para definir las propiedades calculadas de los objetos.
  • Class extends <ClassName> para definir la herencia.
  • This y Super son comandos que tienen funcionalidades especiales dentro de clases.

Function​

Sintaxis​

{local | server} {shared} Function <name>({$parameterName : type; ...}){->$parameterName : type}
// código
nota

No hay palabra clave final para el código de una función. El lenguaje 4D detecta automáticamente el final del código de una función por la siguiente palabra clave Function o el final del archivo de clase.

Las funciones de clase son propiedades específicas de la clase. Son objetos de la clase 4D.Function. En el archivo de definición de clase, las declaraciones de función utilizan la palabra clave Function seguida del nombre de la función.

Si las funciones se declaran en una clase compartida, puede utilizar la palabra clave shared con ellas para que puedan ser llamadas sin la estructura Use...End use. Para obtener más información, consulte el párrafo Funciones compartidas a continuación.

En el contexto de una aplicación cliente/servidor, la palabra clave local o server permite especificar en qué máquina debe ejecutarse la función. Estas palabras claves sólo pueden utilizarse con las funciones del modelo de datos ORDA y las funciones singleton compartidas/sesión. Para más información, consulte el párrafo funciones locales y de servidor más abajo.

In ORDA data model classes and singleton classes, the exposed keyword allows a function to be called by REST requests, and the exposed MCP Function declaration publishes the function as a tool of the MCP server. The MCP keyword is supported in the DataStore class, DataClass and Entity classes, and shared singleton classes. A function declared with MCP but without exposed is not published. For more information, refer to exposed MCP Function.

El nombre de la función debe ser compatible con las reglas de nomenclatura de objetos.

nota

Dado que las propiedades y las funciones comparten el mismo espacio de nombres, no está permitido utilizar el mismo nombre para una propiedad y una función de la misma clase (en este caso se produce un error).

tip

Comenzar el nombre de la función con un caracter guión bajo ("_") excluirá la función de las funcionalidades de autocompletado en el editor de código 4D. Por ejemplo, si declara Function _myPrivateFunction en MyClass, no se propondrá en el editor de código cuando digite en "cs.MyClass. ".

Inmediatamente después del nombre de la función, los parámetros de la función se pueden declarar con un nombre y un tipo de datos asignados, incluido el parámetro de retorno (opcional). Por ejemplo:

Function computeArea($width : Integer; $height : Integer)->$area : Integer

En una función de clase, el comando This se utiliza como instancia del objeto. Por ejemplo:

Function setFullname($firstname : Text; $lastname : Text)
This.firstName:=$firstname
This.lastName:=$lastname

Function getFullname()->$fullname : Text
$fullname:=This.firstName+" "+Uppercase(This.lastName)

Para una función de clase, el comando Current method name devuelve: <ClassName>.<FunctionName>, por ejemplo "MyClass.myFunction".

En el código de la aplicación, las funciones de clases se llaman como los métodos miembros de las instancias de objetos y pueden recibir parámetros si los hay. Se soportan las siguientes sintaxis:

  • uso del operador (). Por ejemplo, myObject.methodName("hello")
  • utilización de un método miembro de la clase "4D.Function":
Aviso de seguridad del hilo

Si una función de clase no es hilo seguro y es llamada por un método con el atributo "Puede ejecutarse en proceso apropiativo":

  • el compilador no genera ningún error (lo que es diferente en comparación con los métodos regulares),
  • un error es lanzado por 4D sólo en tiempo de ejecución.

Parámetros​

Los parámetros de las funciones se declaran utilizando el nombre del parámetro y su tipo, separados por dos puntos. El nombre del parámetro debe cumplir con las reglas de nomenclatura de las propiedades. Múltiples parámetros (y tipos) están separados por punto y coma (;).

Function add($x; $y : Variant; $z : Integer; $xy : Object)
nota

Si no se declaró el tipo, el parámetro se definirá como Variant.

Valor devuelto​

Se declara el parámetro de retorno (opcional) añadiendo una flecha (->) y la definición del parámetro de retorno después de la lista de parámetros de entrada, o dos puntos (:) y el tipo de parámetro de retorno únicamente. Por ejemplo:

Function add($x : Variant; $y : Integer)->$result : Integer
$result:=$x+$y

También puedes declarar el parámetro de retorno añadiendo sólo : type y utilizar la expresión return (también terminará la ejecución de la función). Por ejemplo:

Function add($x : Variant; $y : Integer): Integer
// algo de código
return $x+$y

Ejemplo 1​

property name : Text
property height; width : Integer

// Clase: Rectangle
Class constructor($width : Integer; $height : Integer)
This.name:="Rectangle"
This.height:=$height
This.width:=$width

// Definición de función
Function getArea()->$result : Integer
$result:=(This.height)*(This.width)
// En un método proyecto

var $rect : cs.Rectangle
var $area : Real

$rect:=cs.Rectangle.new(50;100)
$area:=$rect.getArea() //5000

Ejemplo 2​

Este ejemplo utiliza la expresión return:

Function getRectArea($width : Integer; $height : Integer) : Integer
If ($width > 0 && $height > 0)
return $width * $height
Else
return 0
End if

Class constructor​

Sintaxis​

// Class: MyClass
{shared} {{session} singleton} Class constructor({$parameterName : type; ...})
// código
nota

No hay palabra clave final para el código de función class constructor. El lenguaje 4D detecta automáticamente el final del código de una función por la siguiente palabra clave Function o el final del archivo de clase.

Una función constructora de clase acepta parámetros opcionales y puede ser utilizada para crear e inicializar objetos de la clase del usuario.

Cuando llama a la función new(), el constructor de clase es llamado con los parámetros opcionalmente pasados a la función new().

Sólo puede haber una función constructora en una clase (de lo contrario se devuelve un error). El comando Super permite realizar llamadas a superclass, es decir, a la clase padre de la función.

Puede crear y escribir propiedades de instancia dentro del constructor (ver ejemplo). Alternativamente, si los valores de las propiedades de instancia no dependen de los parámetros pasados al constructor, puede definirlos utilizando la palabra clave property.

Utilizando la palabra clave shared se crea una clase compartida, utilizada para instanciar únicamente objetos compartidos. Para obtener más información, consulte el párrafo Clases compartidas.

Utilizando la palabra clave singleton se crea un singleton, utilizado para crear una sola instancia de la sesión. Un session singleton crea una sola instancia por sesión. Para obtener más información, consulte el párrafo Clases singleton.

nota

Las clases de entidad ORDA también pueden beneficiarse de una función Class constructor. La implementación es similar a la de las clases normales pero con algunas diferencias.

Ejemplo​

// Class: MyClass
// Class constructor of MyClass
Class constructor ($name : Text ; $age : Integer)
This.name:=$name
This.age:=$age
// En un método proyecto
// Se puede instanciar un objeto
var $o : cs.MyClass
$o:=cs.MyClass.new("John";42)
// $o = {"name":"John";"age":42}

propiedad​

Sintaxis​

property <propertyName>{; <propertyName2>;...}{ : <propertyType>}

La palabra claveproperty se puede utilizar para declarar una propiedad dentro de una clase usuario. Una propiedad de clase tiene un nombre y un tipo.

La declaración de propiedades de clase mejora las sugerencias del editor de código, las funciones de tecleo predictivo y la detección de errores.

Las propiedades se declaran para nuevos objetos cuando llama a new(), sin embargo, no se añaden automáticamente a los objetos (sólo se añaden cuando se les asigna un valor).

nota

Una propiedad se añade automáticamente al objeto cuando se inicializa en la línea de declaración.

Los nombres de las propiedades deben cumplir las normas de denominación de propiedades.

nota

Dado que las propiedades y las funciones comparten el mismo espacio de nombres, no está permitido utilizar el mismo nombre para una propiedad y una función de la misma clase (en este caso se produce un error).

El tipo de propiedad puede ser uno de los siguientes tipos soportados:

propertyTypeContenido
TextValor texto
DateValor fecha
TimeValor Hora
BooleanValor booleano
IntegerValor entero largo
RealValor real
PointerValor puntero
PictureValor imagen
BlobValeor Blob escalar
CollectionValor colección
VariantValor variant
ObjectObjeto con clase por defecto (4D.object)
4D.<className>Objeto del nombre de la clase 4D
cs.<className>Objeto del nombre de la clase usuario
cs.<namespace>.<className>Objeto del componente <namespace> nombre de la clase

Si omite el tipo en la línea de declaración, la propiedad se crea como una variante.

info

La palabra clave property sólo puede utilizarse en métodos clase y fuera de cualquier bloque Function o Class constructor.

Inicialización de la propiedad en la línea de declaración​

Al declarar una propiedad, tiene la flexibilidad de especificar su tipo de datos y proporcionar su valor en una sola declaración. La sintaxis soportada es:

property <propertyName> { : <propertyType>} := <Propertyvalue>

nota

Cuando se utiliza esta sintaxis, no se pueden declarar varias propiedades en la línea de declaración.

Puede omitir el tipo en la línea de declaración, en cuyo caso el tipo se deducirá cuando sea posible. Por ejemplo:

// Class: MyClass

property name : Text := "Smith"
property age : Integer := 42

property birthDate := !1988-09-29! //se deduce la fecha
property fuzzy //variant

Cuando inicializa una propiedad en su línea de declaración, se agrega al objeto de la clase después de su instanciación con la función new() pero antes de llamar al constructor.

Si una clase extiende a otra, las propiedades de la clase padre se instancian antes que las propiedades de la clase hija.

nota

Si inicializa una propiedad en su línea de declaración con un objeto o una colección en una clase compartida, el valor se transforma automáticamente en un valor compartido:

// en una clase compartida
property myCollection := ["something"]
// myCollection será una colección compartida
// equivalente a:
myCollection := New shared collection("something")

Ejemplo​

// Clase: MyClass

property name : Text
property age : Integer
property color : Text := "Blue"

En un método:

var $o : cs.MyClass
$o:=cs.MyClass.new() //$o:{"color" : "Blue"}
$o.name:="Juan" //$o:{"color" : "Azul"; "name" : "John"}
$o.age:="Smith" //error con la sintaxis de verificación

Function get y Function set​

Sintaxis​

{local | server} {shared} Function get <name>()->$result : type
// código
{local | server} {shared} Function set <name>($parameterName : type)
// código

Function get y Function set son accesos que definen las propiedades calculadas en la clase. Una propiedad calculada es una propiedad nombradas con un tipo de datos que enmascara un cálculo. Cuando se accede a un valor de propiedad calculado, 4D sustituye el código del accesor correspondiente:

  • cuando se lee la propiedad, Function get se ejecuta,
  • cuando se escribe la propiedad, Function get se ejecuta.

Si no se accede a la propiedad, el código nunca se ejecuta.

nota

Las clases de entidad ORDA se benefician de una implementación extendida de propiedades computadas con dos funciones adicionales: query y orderBy.

Las propiedades calculadas están diseñadas para manejar datos que no necesitan ser guardados en memoria. Generalmente se basan en propiedades persistentes. Por ejemplo, si un objeto de clase contiene como propiedad persistente el precio bruto y el tipo de IVA, El precio neto podría ser manejado por una propiedad calculada.

En el archivo de definición de la clase, las declaraciones de propiedades calculadas utilizan las palabras claves Function get (getter) y Function set (setter) seguido por el nombre de la propiedad. El nombre debe cumplir con las reglas de nomenclatura de las propiedades.

Función get devuelve un valor del tipo de la propiedad y Function set toma un parámetro del tipo de la propiedad. Ambos argumentos deben cumplir con los parámetros de función estándar.

Cuando ambas funciones están definidas, la propiedad calculada es read-write. Si solo se define una Function get, la propiedad calculada es de solo lectura. En este caso, se devuelve un error si el código intenta modificar la propiedad. En este caso, se devuelve un error si el código intenta modificar la propiedad.

Si las funciones se declaran en una clase compartida, puede utilizar la palabra clave shared con ellas para que puedan ser llamadas sin la estructura Use...End use. Para obtener más información, consulte el párrafo Funciones compartidas a continuación.

En el contexto de una aplicación cliente/servidor, la palabra clave local o server permite especificar en qué máquina debe ejecutarse la función. Estas palabras claves sólo pueden utilizarse con las funciones del modelo de datos ORDA y las funciones singleton compartidas/sesión. Para más información, consulte el párrafo funciones locales y de servidor más abajo.

El tipo de la propiedad calculada es definido por la declaración de tipo $return del *getter *. Puede ser de cualquier tipo de propiedad válido.

Asignar undefined a una propiedad de objeto limpia su valor mientras se preserva su tipo. Para ello, la Function get es llamada primero para recuperar el tipo de valor, luego Function set es llamado con un valor vacío de ese tipo.

nota

Las clases de entidad ORDA también pueden beneficiarse de una función Class constructor. La implementación es similar a la de las clases normales pero con algunas diferencias.

Ejemplo 1​

//Clase: Person.4dm
property firstName; lastName : Text

Class constructor($firstname : Text; $lastname : Text)
This.firstName:=$firstname
This.lastName:=$lastname

Function get fullName() -> $fullName : Text
$fullName:=This.firstName+" "+This.lastName

Function set fullName( $fullName : Text )
$p:=Position(" "; $fullName)
This.firstName:=Substring($fullName; 1; $p-1)
This.lastName:=Substring($fullName; $p+1)
//en un método proyecto
$fullName:=$person.fullName // Function get fullName() is called
$person.fullName:="John Smith" // Function set fullName() is called

Ejemplo 2​

Function get fullAddress()->$result : Object

$result:=New object

$result.fullName:=This.fullName
$result.address:=This.address
$result.zipCode:=This.zipCode
$result.city:=This.city
$result.state:=This.state
$result.country:=This.country

Class extends <ClassName>​

Sintaxis​

// Class hijo
Class extends <ParentClass>

La palabra clave Class extends se utiliza en la declaración de clase para crear una clase usuario que es hijo de otra clase usuario. La clase hijo hereda todas las funciones de la clase padre.

La extensión de clase debe respetar las siguientes reglas:

  • Una clase de usuario no puede extender una clase integrada (excepto las 4D.Object y clases ORDA que se extienden por defecto para las clases de usuario).
  • Una clase usuario no puede extender una clase usuario de otro proyecto o componente.
  • Una clase usuario no puede extenderse a sí misma.
  • No es posible extender las clases de una manera circular (es decir, "a" extiende "b" que extiende "a").
  • No es posible definir una clase usuario compartida extendida a partir de una clase usuario no compartida.

La ruptura de tal regla no es detectada por el editor de código o el intérprete, solo el compilador y comprobar sintaxis arrojará un error en este caso.

Una clase extendida puede llamar al constructor de su clase padre utilizando el comando Super.

Ejemplo​

Este ejemplo crea una clase llamada Square de una clase llamada Polygon.

//Clase: Square

//ruta: Classes/Square.4dm

Class extends Polygon

Class constructor ($side : Integer)

Llama al constructor de la clase padre con las longitudes
// suministradas para el ancho y alto del polígono
Super($side;$side)
// En las clases derivadas, Super debe ser llamado antes de
// utilizar 'This'
This.name:="Square"



Function getArea() -> $area : Integer
$area:=This.height*This.width

Function comments​

You can document a class function or a class constructor with comment lines written right above its Function or Class constructor declaration. 4D reads this comment block and shows it in the code assistance of the 4D code editor and of VS Code (completion details, help tips, and signature help), together with the function signature. The same comments also provide the MCP tool metadata of the function.

The comment block follows these rules:

  • It is made of // lines and/or a /* */ block. Each line of a /* */ block is handled like a // line, and the /* and */ markers are not displayed.
  • It must be placed immediately above the declaration. A blank line between the comment block and the declaration breaks the association: only the signature is displayed.
  • Each comment block is associated only with the next declaration, even when it is written right after the code of the previous function.
  • Only the saved code is read: after you edit a comment, the code assistance shows the new text once the class is saved.
  • The text is displayed as plain text: Markdown markers such as ** or * are shown as written.

Each line of the block is interpreted according to its beginning:

LíneaSignificado
// <text>Descripción de la función. Several description lines are combined in order, with their line breaks. Empty // lines at the start or at the end of the block are ignored.
// $name: <text>Description of the $name parameter.
// {$name}: <text>Description of the $name parameter, marked as optional for MCP.
// ...: <text>Description of the variadic parameters (... : Tipo).
// param: $name <text>, // param: {$name} <text>, // param: ... <text>Same as $name:, {$name}:, and ...: lines.
// MCP: <text>Description of the function for MCP only. Several MCP: description lines are combined in order.
// MCP: @tag {@tag} or // @tag {@tag}MCP behavior tags (see MCP tool metadata). The line must contain only supported tags, otherwise it is a description line.

A $name: or {$name}: line is a parameter description only if $name is a parameter of the function signature. Otherwise, it is a description line (for example // $120: price in dollars). A ...: line is a parameter description only if the function has variadic parameters.

The code assistance displays the description lines, then a blank line, then the parameter lines as written. MCP: lines are never displayed in the code assistance.

When you compile the project, the compiler reports a warning for each parameter of the signature that has no $name: or {$name}: line in the comment block, and for contradictory MCP behavior tags. Functions without a comment block are not checked.

nota

Comment blocks are supported in project classes (including shared and singleton classes) and in component classes. For compiled components, the descriptions are provided by the syntax file.

Ejemplo 1​

You want to document a function and its parameter so that they are displayed in the code editor:

// Class: Invoice

// Returns the price with taxes
// The rate is a percentage
// $rate: tax rate to apply (20 for 20%)
Function priceWithTaxes($rate : Real) : Real
return This.price*(1+($rate/100))

When you type $invoice.priceWithTaxes( in a method, the help displays the signature with:

Returns the price with taxes
The rate is a percentage

$rate: tax rate to apply (20 for 20%)

Ejemplo 2​

You want to document a class constructor with a /* */ block:

// Class: Counter

/*
Creates a counter starting at a value
$start: initial value of the counter
*/
Class constructor($start : Integer)
This.value:=$start

The description and the $start: line are displayed when you type cs.Counter.new(.

MCP tool metadata​

The comment blocks of exposed MCP Function declarations also describe these functions as MCP tools. When the project is compiled, 4D generates the Project/DerivedData/syntaxMCP.json file, which contains the metadata of these tools in a toolList array.

Only functions of the following classes are listed:

  • the DataStore class,
  • DataClass and Entity classes,
  • clases Singleton.

Functions of EntitySelection classes and of other classes, as well as functions that are not declared exposed MCP Function, are not listed, even if their comment block contains MCP: lines.

nota

If a Resources/syntaxMCP.json file exists in the project, it is used instead of the generated file. The compiler never overwrites it. See Resources folder.

The MCP server of 4D builds its tool catalog from the same comment blocks and the same type conversions, using the running code: in interpreted mode, a function you add or modify is published at the next tools/list request, without restarting. The catalog of the MCP server also adds the elements needed to call the tools, such as a key argument for Entity class functions and "additionalProperties": false in inputSchema (see Tool catalog). The following functions are not published, and 4D logs a message:

  • a function whose tool name is longer than 64 characters, contains characters other than letters, digits, _ and -, or is the name of another tool (including a built-in CRUD tool),
  • an Entity class function that declares a $key parameter,
  • a function that declares both a $args parameter and variadic parameters.

A function without description is published, and 4D logs a warning.

Each tool of the toolList array is an object with the following properties:

PropiedadDescripción
nameName of the tool: the function name for a DataStore class function, ClassName-functionName for other classes
descripciónText of the MCP: description lines, or the description lines if there is no MCP: description. Omitted if there is no description
inputSchema{"type": "object", "properties": {...}, "required": [...]}: one property per parameter of the signature, named without the $, with the parameter comment as description
outputSchemaSchema of the return type. Omitted if the function returns nothing or a Variant
annotationsBehavior hints derived from the MCP: behavior tags

In inputSchema:

  • every parameter is listed in required, except those documented with a {$name}: line. required is omitted if it is empty.
  • typed variadic parameters (... : Type) are represented by an optional args property of type array, whose items have the schema of Type. The ...: line is its description.

Parameter and return types are converted to JSON Schema as follows:

4D typeJSON Schema
Text{"type": "string"}
Integer{"type": "integer"}
Real{"type": "number"}
Boolean{"type": "boolean"}
Fecha{"type": "string", "format": "date"}
Time{"type": "integer", "description": "duration in seconds"}
Object, class instance{"type": "object"}
Collection{"type": "array"}
Blob, 4D.Blob, Picture{"type": "string", "contentEncoding": "base64"}
Variant, or no type{}

The supported behavior tags are converted to annotations as follows:

TagAnnotation
@readOnly"readOnlyHint": true
@destructive"destructiveHint": true
@additive"destructiveHint": false
@idempotent"idempotentHint": true
@openWorld"openWorldHint": true

openWorldHint is always present: it is false if @openWorld is not used. Other hints are omitted when their tag is not used. @readOnly, @destructive, and @additive contradict each other: if a function uses two or more of them, the compiler reports a warning and the corresponding hints are omitted. Unknown tags are ignored.

Ejemplo 3​

You document the MCP tools of a DateTime singleton class:

// Class: DateTime

singleton Class constructor()
This.date:=Current date

// Adds a number of days to the date
// MCP: Add days to the date-time
// $days: number of days to add (negative to subtract)
exposed MCP Function addDays($days : Integer) : Date
This.date:=Add to date(This.date; 0; 0; $days)
return This.date

// Adds zero or more day offsets to the date and returns the updated date
// MCP: Add day offsets to the date-time
// MCP: @idempotent
// ...: day offsets to add (negative to subtract)
exposed MCP Function addDayOffsets(... : Integer) : Date
var $offsetIndex; $totalDays : Integer
For ($offsetIndex; 1; Count parameters)
$totalDays+=${$offsetIndex}
End for
This.date:=Add to date(This.date; 0; 0; $totalDays)
return This.date

After compilation, Project/DerivedData/syntaxMCP.json contains:

{
"toolList": [
{
"name": "DateTime-addDays",
"description": "Add days to the date-time",
"inputSchema": {
"type": "object",
"properties": {
"days": { "type": "integer", "description": "number of days to add (negative to subtract)" }
},
"required": ["days"]
},
"outputSchema": { "type": "string", "format": "date" },
"annotations": { "openWorldHint": false }
},
{
"name": "DateTime-addDayOffsets",
"description": "Add day offsets to the date-time",
"inputSchema": {
"type": "object",
"properties": {
"args": {
"type": "array",
"items": { "type": "integer" },
"description": "day offsets to add (negative to subtract)"
}
}
},
"outputSchema": { "type": "string", "format": "date" },
"annotations": { "idempotentHint": true, "openWorldHint": false }
}
]
}

Ejemplo 4​

You want to read the names of the MCP tools generated by the last compilation:

var $file : 4D.File:=File("/PACKAGE/Project/DerivedData/syntaxMCP.json")
var $metadata : Object:=JSON Parse($file.getText())
var $names : Collection:=$metadata.toolList.extract("name")
// $names is ["DateTime-addDays";"DateTime-addDayOffsets"]

Ejemplo 5​

You document a tool with param: lines and a behavior tag line without the MCP: prefix:

// Class: Stock

shared singleton Class constructor()

// Returns the quantity in stock of a product
// param: $reference reference of the product
// param: {$warehouse} code of the warehouse (all warehouses if omitted)
// @readOnly
exposed MCP Function getQuantity($reference : Text; $warehouse : Text) : Integer
If ($warehouse="")
return ds.Stock.query("reference = :1"; $reference).sum("quantity")
End if
return ds.Stock.query("reference = :1 and warehouse = :2"; $reference; $warehouse).sum("quantity")

The Stock-getQuantity tool has a required reference property, an optional warehouse property, and the "readOnlyHint": true annotation.

Comandos de función clase​

Los siguientes comandos tienen características específicas cuando se utilizan dentro de las funciones clase:

Super​

El comando Super permite realizar llamadas a superclass, es decir, a la clase padre de la función. Sólo puede haber una función constructora en una clase (de lo contrario se devuelve un error).

Para más detalles, vea la descripción del comando Super.

This​

El comando This devuelve una referencia al objeto procesado actualmente. En la mayoría de los casos, el valor de This está determinado por cómo se llama una función clase. Normalmente, This se refiere al objeto al que la función fue llamada, como si la función estuviera sobre el objeto.

Ejemplo:

//Clase: ob

Function f() : Integer
return This.a+This.b

A continuación, puede escribir en un método:

$o:=cs.ob.new()
$o.a:=5
$o.b:=3
$val:=$o.f() //8

Para más detalles, vea la descripción del comando This.

OB Class​

OB Class ( object ) -> Object | Null​

OB Class devuelve la clase del objeto pasado como parámetro.

OB Instance of​

OB Instance of ( object ; class ) -> Boolean​

OB Instance of devuelve true si object pertenece a la class o a una de las clases heredadas y false de lo contrario.

Clases compartidas​

Puede crear clases compartidas. Una clase compartida es una clase usuario que instancia un objeto compartido cuando se llama a la función new() en la clase. Una clase compartida sólo puede crear objetos compartidos.

Las clases compartidas también admiten funciones compartidas que pueden llamarse sin estructuras Use...End use.

La propiedad .isShared de los objetos de la Clase permite saber si la clase está compartida.

info
  • Una clase que hereda de una clase no compartida no puede definirse como compartida.
  • Las clases compartidas no están soportadas por las clases basadas en ORDA.

Creación de una clase compartida​

Para crear una clase compartida, añada la palabra clave shared antes del Class constructor. Por ejemplo:

//shared class: Person
shared Class constructor($firstname : Text; $lastname : Text)
This.firstName:=$firstname
This.lastName:=$lastname

//myMethod
var $person := cs.Person.new("John"; "Smith")
OB Is shared($person) // true
cs.Person.isShared //true

Funciones compartidas​

Si una función definida al interior de una clase compartida modifica objetos de la clase, debería llamar a la estructura Use...End use para proteger el acceso a los objetos compartidos. Sin embargo, para simplificar el código, puede definir la función como compartida, de modo que active automáticamente un Use...End use interno cuando se ejecute.

Para crear una función compartida, añada la palabra clave shared antes de la palabra clave Function en una clase compartida. Por ejemplo:

//clase compartida Foo
shared Class constructor()
This.variable:=1

shared Function Bar($value : Integer)
This.variable:=$value //no es necesario llamar use/end use
nota

Si se utiliza la palabra clave shared en una clase usuario no compartida, se ignora.

Clases Singleton​

Una clase singleton es una clase usuario que sólo produce una única instancia. Para más información sobre el concepto de singletons, por favor consulte la página Wikipedia sobre los singletons.

Tipos de Singletons​

Los singletons son útiles para definir los valores que necesitan estar disponibles desde cualquier parte de una aplicación, una sesión o un proceso.

4D soporta tres tipos de singletons:

  • un singleton proceso tiene una instancia única para el proceso en el que se instancia,
  • un singleton compartido tiene una instancia única para todos los procesos en la máquina.
  • un singleton de sesión es un singleton compartido pero con una instancia única para todos los procesos en la sesión. Los singletons de sesión son compartidos dentro de una sesión completa, pero varían entre sesiones. En el contexto de un cliente-servidor o una aplicación web, los singletons de sesión hacen posible crear y utilizar una instancia diferente para cada sesión, y por lo tanto para cada usuario. Los singletons de sesión son particularmente apropiados con aplicaciones Qodly.
info

Las clases Singleton no están soportadas por las clases ORDA.

La siguiente tabla indica el alcance de una instancia singleton dependiendo de donde se creó:

Singleton creado enAlcance del proceso singletonAlcance del singleton compartidoAlcance del singleton de sesión
4D monopuestoProcesoAplicaciónAplicación o sesión Web/REST
4D ServerProcesoMáquina 4D ServerSesión cliente/servidor o sesión Web/REST o sesión de procedimiento almacenado
Modo remoto 4DProceso (nota: los singletons no están sincronizados en el proceso gemelo)Máquina remota 4DMáquina remota 4D o sesión Web/REST

Una vez instanciado, existe una clase singleton (y su singleton) siempre que exista una referencia a ella en algún lugar de la aplicación que se ejecuta en la máquina.

Crear y utilizar singletons​

Se declaran clases singleton añadiendo la(s) palabra(s) clave(s) apropiada(s) antes del Class constructor:

  • Para declarar una clase singleton (proceso), escriba singleton Class constructor().
  • Para declarar una clase singleton compartida, escribe shared singleton Class constructor().
  • Para declarar una clase singleton de sesión, escriba session singleton Class constructor().
nota
  • Los singletons de sesión son automáticamente singletons compartidos (no hay necesidad de usar la palabra clave shared en el constructor de clases).
  • Las funciones compartidas Singleton soportan palabra clave onHTTPGet.

El singleton de la clase se instanciará en la primera llamada de la propiedad cs.<class>[.me. El singleton instanciado de la clase se devuelve siempre cuando se utiliza la propiedad me.

Si necesita instanciar un singleton con parámetros, también puede llamar la función new(). En este caso, se recomienda instanciar el singleton en algún código ejecutado al inicio de la aplicación.

La propiedad isSingleton de los objetos Clase permite saber si la clase es un singleton.

La propiedad .isSessionSingleton de los objetos Class permite saber si la clase es un singleton de sesión.

Funciones singleton expuestas​

Las funciones singleton compartidas y de sesión soportan la palabra clave exposed. Una función singleton expuesta puede ser llamada directamente por peticiones REST. Esta función es útil para diseñar páginas Qodly que llamen a las funciones 4D.

Ejemplos​

Singleton Proceso​

//class: ProcessTag
singleton Class constructor()
This.tag:=Random

Para utilizar el singleton:

//en otro proceso
var $mySingleton := cs.ProcessTag.me //Primera instanciación
//$mySingleton.tag = 5425 por ejemplo
...
var $myOtherSingleton := cs.ProcessTag.me
//$myOtherSingleton.tag = 5425

//en otro proceso
var $mySingleton := cs.ProcessTag.me //Primera instanciación
//$mySingleton.tag = 14856 por ejemplo
...
var $myOtherSingleton := cs.ProcessTag.me
//$myOtherSingleton.tag = 14856

Singleton compartido​

//Class VehicleFactory

property vehicleBuilt : Integer

shared singleton Class constructor()
This.vehicleBuilt := 0 //Número de vehículos construidos por la fábrica

shared Function buildVehicle ($type : Text) -> $vehicle : cs.Vehicle

Case of
: $type="car"
$vehicle:=cs.Car.new()
: $type="truck"
$vehicle:=cs.Truck.new()
: $type="sport car"
$vehicle:=cs.SportCar.new()
: $type="motorbike"
$vehicle:=cs.Motorbike.new()
Else
$vehicle:=cs.Car.new()
End case
This.vehicleBuilt+=1

Luego puede llamar al singleton cs.VehicleFactory para obtener un nuevo vehículo desde cualquier lugar de la aplicación en su máquina con una sola línea:

$vehicle:=cs.VehicleFactory.me.buildVehicle("truck")

Dado que la función buildVehicle() modifica el singleton cs.VehicleFactory (incrementando This.vehicleBuilt), debe agregar la palabra clave shared.

Singleton de sesión​

En una aplicación de inventario, desea implementar un inventario de artículos utilizando singletons de sesión.

//class ItemInventory

property itemList : Collection:=[]

session singleton Class constructor()

shared function addItem($item:object)
This.itemList.push($item)

Al definir la clase ItemInventory como un singleton de sesión, asegúrese de que cada sesión y por lo tanto cada usuario tiene su propio inventario. Acceder al inventario del usuario es tan simple como:

//en una sesión usuario
$myList := cs.ItemInventory.me.itemList
//lista de elemento del usuario actual

local y server​

En la arquitectura cliente-servidor, las palabras clave local y server permiten especificar dónde se desea que se ejecute la función: en el lado del cliente o en el lado del servidor. Controlar la ubicación de ejecución es útil por razones de rendimiento o para implementar características de lógica de negocio.

La sintaxis formal es:

// declarar una función a ejecutar localmente en un cliente en el marco del cliente/servidor
local Function <functionName>
// declarar una función para ejecutar en el servidor en cliente/servidor
server Function <functionName>

Las palabras claves local y server solo están disponibles para las funciones de las siguientes clases:

Generalidades​

Las funciones compatibles tienen una ubicación de ejecución predeterminada cuando no se utiliza ninguna palabra clave de ubicación. No obstante, puede insertar una palabra clave local o server para modificar la ubicación de ejecución, o para hacer el código más explícito.

Funciones soportadasEjecución por defectocon la palabra clave localcon la palabra clave server
ORDA data modelen el servidorLa función se ejecuta en el cliente si se llama en el cliente
Singleton compartido o de sesiónLocalLa función se ejecuta en el servidor en la instancia de servidor del singleton.
Si no hay ninguna instancia del singleton en el servidor, se crea una.

Si las palabras clave «local» y «server» se utilizan en otro contexto, se devuelve un error.

nota

Para una descripción general de dónde se ejecuta realmente el código en cliente/servidor, consulte esta sección.

local​

En una arquitectura cliente-servidor, la palabra clave local indica que la función debe ejecutarse en el equipo desde el que se llama.

Recordatorio

La palabra clave «local» no tiene sentido en el caso de las funciones compartidas o singleton de sesión, ya que estas se ejecutan de forma local por defecto.

Por defecto, las funciones del modelo de datos ORDA se ejecutan en el servidor. Suele ofrecer el mejor rendimiento, ya que sólo se envían por la red la petición de función y el resultado. Sin embargo, por motivos de optimización, es posible que desee ejecutar una función del modelo de datos en el cliente. A continuación, puede utilizar la palabra clave local.

Ejemplo: cálculo de la edad​

Dada una entidad con un atributo birthDate, queremos definir una función age() que sería llamada en un list box. Esta función puede ejecutarse en el cliente, lo que evita lanzar una petición al servidor para cada línea del list box.

En la classe StudentsEntity:

Class extends Entity

local Function age() -> $age: Variant

If (This.birthDate#!00-00-00!)
$age:=Year of(Current date)-Year of(This.birthDate)
Else
$age:=Null
End if

server​

En una arquitectura cliente/servidor, la palabra clave server especifica que la función debe ejecutarse en el lado del servidor.

Recordatorio

La palabra clave server es inútil para las funciones del modelo de datos ORDA, que se ejecutan en el servidor por defecto.

Los parámetros y el resultado de la función server deben ser streamable. Por ejemplo, 4D.Datastore, File handle o WebServer son clases no transmisibles, pero 4D.File sí es transmisible.

Esta funcionalidad resulta especialmente útil en el contexto de las sesiones de usuario remotas, ya que permite implementar la lógica de negocio en un singleton de sesión para compartirla entre todos los procesos de la sesión, ampliando así las funcionalidades del comando Session. En este caso, es posible que desee que la lógica de negocio relevante se ejecute en el servidor para que toda la información de la sesión se recopile en el servidor.

Por defecto, las funciones singleton compartidas o de sesión se ejecutan localmente. Añadir la palabra clave server en la definición de la función de la clase hace que 4D utilice la instancia singleton en el servidor. Tenga en cuenta que esto puede dar lugar a una instanciación del singleton en el servidor si aún no existe ninguna instancia.

Para las sesiones singletons, la función se ejecuta en el servidor en la instancia de singleton correspondiente, es decir la instancia de singleton para la sesión actual.

nota

Si declara una server Function en un singleton compartido, entonces:

  • instancia un singleton S1 en el cliente (llamado s1),
  • ejecuta s1.function() en el cliente.

Si no existe una instancia de S1 en el servidor en ese momento, S1 es instanciado en el servidor (el constructor se ejecuta), y function() se ejecuta en esa instancia del servidor. Como resultado, pueden coexistir dos instancias de S1 (del lado del cliente y del lado del servidor), con valores de propiedad distintos. En este caso, siempre se accede a s1.property de forma local. No se puede acceder a él en el servidor; por ejemplo, desde código del lado del servidor utilizando la notación de punto directa (se devuelve un error).

Ejemplo: singleton Administration​

El singleton compartido Administration tiene una función "server" que ejecuta el comando Process activity. Este singleton se instancia en un 4D remoto, pero la función devuelve la actividad del servidor en el propio servidor.

// Administration class

shared singleton Class constructor

// Esta función se ejecuta en el servidor
server Function processActivity() : Object
return Process activity


Function localProcessActivity() : Object
return Process activity

Código que se ejecuta en el cliente:

var $localActivity; $serverActivity : Object
var $administration : cs.Administration

// El singleton Administration se instancia en el cliente 4D
$administration:=cs.Administration.me

// Obtiene los procesos en ejecución en el 4D remoto
$localActivity:=$administration.localProcessActivity()

// Obtiene los procesos y sesiones en ejecución en el servidor 4D
$serverActivity:=$administration.processActivity()

Ejemplo: singleton de sesión​

Almacenas a sus usuarios en una tabla Users y gestionas una autenticación personalizada. Utiliza un singleton de sesión para la autenticación:

// Classe singleton session UserSession

server Function checkUser($credentials : Object) : Boolean

var $user : cs.UsersEntity
var $result:=False

If ($credentials#Null)
$user:=ds.Users.query("Email === :1"; $credentials.identifier).first()

If (($user#Null) && (Verify password hash($credentials.password; $user.Password)))
Use (Session.storage)
Session.storage.userInfo:=New shared object("userId"; $user.ID)
End use

$result:=True
End if
End if

return $result

Para proporcionar el usuario actual a los clientes 4D, el singleton expone una propiedad calculada del usuario obtenida del servidor:

server Function get user() : cs.UsersEntity
return ds.Users.get(Session.storage.userInfo.userId)