Fecha de revisión: 25/Feb/2005

Supongo que todos quienes han diseñado o participado en el diseño de un lenguaje, tendrán distintos motivos para haberse embarcado en tal aventura. En mi caso, la inspiración me llegó al conocer la rebuscada sintaxis añadida a Delphi para declarar campos y métodos estáticos, compatibles con la plataforma .NET. En este artículo explicaré la estructura de una clase en lo que respecta a sus niveles de visibilidad, la separación entre recursos estáticos y a nivel de instancia, y los modificadores aplicables a las declaraciones de métodos.

VISIBILIDAD

Delphi ha ofrecido tradicionalmente los tres niveles clásicos de visibilidad, inspirados en el modelo de objetos de C++. Aparte de ellos, ofrece published, que es una variante de public con información adicional de tipos en tiempo de ejecución (RTTI), y durante poco tiempo jugueteó con automated, un invento apresurado y chapucero para tratar con Automatización OLE, que fue declarado obsoleto, pernicioso y sumamente contagioso a partir de Delphi 3.

En contraste, el modelo de objetos de .NET exige al menos cinco niveles de visibilidad. Algunos nombres de niveles se utilizan en ambos modelos, pero el significado difiere un poco. Lo que hizo Delphi.NET fue adaptar y extender su propio modelo, en vez de adoptar directamente el nuevo modelo, en aras de la compatibilidad con el código para Windows nativo. Freya conserva, a regañadientes, los cinco niveles de visibilidad existentes en Delphi.NET. A saber:

  • private: Equivalente al internal de C#. El recurso puede ser utilizado solamente desde otras clases definidas en la misma unidad de compilación.
  • strict private: Equivalente al private de C#. El recurso sólo puede ser utilizado desde la propia clase.
  • protected: Equivalente al protected internal de C#. El recurso puede ser utilizado desde otras clases definidas en la misma unidad de compilación, y en clases derivadas.
  • strict protected: Equivalente al protected de C#. El recurso sólo puede ser utilizado por la propia clase y sus clases derivadas.
  • public: Equivalente al public de C#. Barra libre. Sírvase usted.

Por supuesto, desaparece la sección published de Delphi, que fue un error de diseño desde el principio, al mezclar dos conceptos diferentes como información de tipos en tiempo de ejecución y visibilidad. Uno de las consecuencias más nocivas de published es que cuanto componente se deja caer sobre un módulo de datos, puede ser usado y modificado desde cualquier otra parte de la aplicación, dificultando el encapsulamiento correcto de la funcionalidad de ésta.

CLASES E INSTANCIAS

Como he mencionado al principio, la "culpa" de la existencia de Freya la tiene, en su origen, la sintaxis utilizada por Delphi para declarar campos y métodos estáticos. Primero, es necesario aclarar la diferencia entre un método estático y un método de clase, porque estos últimos ya existían en Delphi nativo. Aunque ambos tipos de métodos se aplican sobre un nombre de clase, en el caso del método estático no hay un parámetro oculto Self, como en los métodos de instancias; los métodos de clase utilizan un parámetro oculto Self, aunque éste apunta a una referencia de clase, en vez de apuntar a un objeto. Los métodos de clase se declaraban en Delphi (y todavía se declaran) añadiendo un prefijo a la declaración:

class procedure MetodoClase(Parametro: Integer);
NOTA
Los campos estáticos, además, no tenían siquiera un antecedente de este tipo en Delphi clásico, que obligaba a usar variables globales a modo de sustitución.

Al parecer, cuando el equipo de Delphi.NET tuvo que extender la sintaxis de la clase para permitir recursos estáticos, se tomaron como punto de partida las secciones de declaraciones clásicas de Pascal, y se decidió que lo apropiado era anidar estas secciones dentro de una declaración de clase:

// ATENCION: Delphi.NET
type
    AClass = class
    private
        var
            // Campos "normales", a nivel de instancia
            Field1, Field2: Integer;
            Field3, Field4: string;
        class var
            // Campos estáticos, a nivel de clase
            Field5, Field6: Double;
            Field7: Boolean;
    private
        // Todavía se permite el estilo "antiguo"
        Field8: Integer;
    protected
        // ¡Los métodos estáticos siguen reglas muy diferentes!
        class function DoThat(Value: Integer): string; static;
        ...
    end;

Si no ha pegado un salto al leer este listado, es que tiene usted una piel envidiablemente gruesa. Estos son algunos de los problemas con este tipo de declaraciones:

  1. Para empezar, hay dos formas de declarar campos a nivel de instancia: al modo tradicional, e incluyéndolos dentro de una sección var. Explíquele a un estudiante de programación que existen dos técnicas, que es preferible usar siempre la primera de ellas, y que cuando vea un listado que utilice la segunda, no se apresure a deducir que se trata de un error sintáctico...
  2. ¿No le parece demasiado anidamiento, para el estilo sintáctico de Pascal? Es cierto que la sintaxis de los registros variantes (el infame record case) es infinitamente peor. Pero para nuestra fortuna, casi ha pasado a la historia (excepto en los message crackers al estilo Microsoft).
  3. La técnica para declarar un campo estático es muy diferente a la utilizada para declarar un método estático.
  4. A su vez, la técnica para declarar un método estático ¡es completamente diferente a la utilizada para declarar métodos de clases, un tipo de recurso muy cercano! Mientras a un método de clase le basta el prefijo class, un método estático exige el sufijo static.
  5. Para rematar, un método estático exige redundantemente la presencia del prefijo class.

Y hay más razones en contra de esta sintaxis. Podemos interpretar que los métodos y campos estáticos son en realidad miembros de instancia de otra clase asociada a la clase principal, para la cual solamente se crea una sola instancia. Bajo esta luz, al declarar una clase con miembros estáticos, estamos realmente declarando dos clases, una de las cuales sería un singleton subordinado. De hecho, la implementación de los métodos de clase tradicionales en Delphi.NET aprovecha internamente esta interpretación. Si aceptamos este punto de vista, y quisiéramos establecer una clasificación jerárquica de los distintos tipos de recursos declarables en una clase, obtendríamos el siguiente árbol de categorías como clasificación más natural:

1. Todas las declaraciones
    1.1. Declaraciones a nivel de instancia
        1.1.1. Secciones de visibilidad
    1.2. Declaraciones estáticas
        1.2.1. Secciones de visibilidad

Es decir, en la interpretación más "natural" (dicho sea con todas las precauciones), el anidamiento de declaraciones tendría que tener lugar ¡en el sentido contrario al usado en Delphi.NET!

De todos modos, tanto anidamiento es incómodo, sin importar el orden en que tenga lugar. La solución adoptada por Freya está situada a mitad de camino entre lo que sería natural en Pascal y lo que hacen Java y C#: mezclamos los atributos de visibilidad con el "nivel" de la declaración, pero sacamos factor común para toda una sección. Es mejor ilustrarlo mediante un ejemplo:

// Freya
MiClase = class
protected
    ...
public
    ...
static strict private
    ...
static public
    ...
end.

Cada sección de visibilidad puede ir precedida, o no, por el modificador static. Cuando este modificador está presente, todas las declaraciones de la sección se consideran estáticas. Los métodos de clase pueden declararse en cualquier sección de la clase, y siguen identificándose mediante el prefijo class.

NOTA
La sintaxis de los constructores se trata en este otro artículo, junto con las referencias de clases. Freya incluye los constructores "tradicionales", los constructores virtuales de Delphi, y los constructores estáticos de clases de .NET.

MODIFICADORES DE METODOS

Veamos ahora la sintaxis formal de una declaración de método en Freya:

[class] (procedure|function) method-identifier
    [parameters][: return-type]; [modifiers ;]

La parte más novedosa es la lista de modificadores que puede ir asociada a un método. Hay cinco modificadores disponibles en Freya, y de ellos, hay un grupo de cuatro modificadores mutuamente excluyentes:

  1. virtual: Se utiliza la primera vez que introducimos un método "redefinible" en una jerarquía de clases.
  2. abstract: Es una alternativa de virtual, e indica que el método no tiene aún una implementación. Todo método abstracto es necesariamente virtual. Sin embargo, Delphi obliga a incluir tanto abstract como virtual en estos casos.
  3. override: Se utiliza cuando se redefine un método virtual en una clase derivada.
  4. sealed: Un método virtual definido por algún ancestro, se redefine por última vez.

Aparte de estos cuatro modificadores, Freya utiliza también el modificador new, que puede utilizarse aislado o en compañía de virtual o abstract. new tiene el mismo significado que en C#, y aproximadamente el mismo que reintroduce en Delphi clásico: se debe incluir cuando un nuevo método enmascara un método similar existente en un ancestro. El orden relativo de los modificadores no es significativo: da lo mismo new virtual que virtual new.

Note, de paso, que Freya no necesita la directiva overload. El CLR de .NET ofrece soporte nativo para la sobrecarga de nombres, y no es necesario que el compilador utilice la conocida técnica de name mangling. Por el contrario, los lenguajes como Eiffel, que no soportan la sobrecarga de nombres de métodos, son los que han tenido que realizar adaptaciones para poder acceder a métodos sobrecargados definidos en otros lenguajes.

PARAMETROS

No hay sorpresas en la sintaxis de los parámetros formales y el opcional tipo de retorno, excepto que desaparecen muchas opciones tradicionales, como los parámetros sin tipo. Los modificadores out y var se utilizan para los parámetros de salida y los parámetros por referencia. No existe un equivalente en Freya del modificador params de C#... porque es innecesario. A cambio, Freya hereda de Delphi la posibilidad de construir vectores "al vuelo":

// Freya
System.Console.WriteLine('{1}, {0}', [Cliente.Nombre, Cliente.Apellidos]);

C# necesita params porque el constructor de vectores es mucho más engorroso:

// C#
System.Console.WriteLine("{1}, {0}", new object[] {Cliente.Nombre, Cliente.Apellidos});

Por último, Freya no exige que repitamos el modificador var al pasar un parámetro por referencia a un método, como sí ocurre en C#. Esta medida adoptada por C# mejora la legibilidad del código, pero va totalmente en contra del estilo histórico de Pascal y sus derivados... y obliga a teclear un poco más. Freya permite la repetición del modificador var al aplicar métodos con parámetros pasados por referencia, pero no obliga a ello.

NOTA
He omitido deliberadamente cualquier referencia a clases y métodos genéricos en este artículo.

Vea también:

Iteradores en Freya Propiedades en Freya
Excepciones en Freya Constructores en Freya
Interfaces en Freya