lunes, diciembre 31, 2007

Ornitorrinco

... and the mome raths outgrabe.No todo lo que camina como un pato, nada como un pato y pone huevos como un pato, tiene necesariamente que ser un pato. Podría tratarse de un ornitorrinco, ¿verdad?
Suponga que yo le digo que esto es Freya:
IStack = interface[X]
Count: Integer;
Top: X;
method Push(Value: X);
method Pop;
requires Count > 0;
    IsEmpty: Boolean => Count = 0;
end;
Para que le resulte más fácil detectar el elemento extraño, utilizaré una sintaxis equivalente, algo más verbosa:
IStack = interface[X]
property Count: Integer;
property Top: X;
method Push(Value: X);
method Pop;
requires Count > 0;
    property IsEmpty: Boolean;
begin
Result := Count = 0;
end;
end;
Y ahora, antes de subir la otra mitad del artículo, dígame si lo anterior le parece bien o mal...

Efectivamente, como mencionaba Marto en un comentario, se supone que las interfaces no "implementan". Sin embargo, es precisamente ese efecto lo que logran los métodos de extensión de C# 3.0:
interface IStack<X>
{
int Count { get; }
X Top { get; }
void Push(X value);
void Pop();
}
static class StackExt
{
public static bool IsEmpty<X>(this IStack<X> s)
{
return s.Count == 0;
}
}
Gracias a estos métodos, es posible escribir código como el siguiente, suponiendo que tenemos ya una clase que implementa el tipo de interfaz:
IStack<int> st = new StackImpl<int>();
if (st.IsEmpty()) { ... }
¿Es bueno, o es malo? No veo nada malo en permitir manipulaciones predefinidas sobre el estado "público" ofrecido por el contrato de la interfaz: lo mismo se puede lograr, incluso sin métodos de extensión, aunque al precio de escribir muchísimo más.
Ahora bien, mi duda va más allá. ¿Merece la pena implementar métodos de extensión (Freya ya los tiene), o sería suficiente con añadir al lenguaje implementaciones predefinidas en interfaces, como la del ejemplo de Freya? El caso es que los métodos de extensión son nada elegantes:
  • El mecanismo de selección es horrible: basta con introducir una cláusula using para activarlos, dándole un protagonismo inapropiado a estas cláusulas.
  • Estos métodos, cuando se emplean para extender una clase, plantean un grave problema de estabilidad respecto al versionado: si el implementador de la clase introduce un método de instancia en la clase extendida, dejará de funcionar la extensión, porque el método de instancia tiene la prioridad. Lo peor es que sería muy difícil detectar estos problemas.
De momento, a falta de más análisis, me inclino por sustituir este recurso por implementaciones en interfaces. Es verdad que no son recursos equivalentes, pero la suciedad de los métodos de extensión me aterra.

Etiquetas: , , ,

6 Comments:

Blogger Marto said...

Buenas Ian,

Pues en principio me parecería mal, ya que estás dando la implementación del método de lectura de la property IsEmpty en una interfaz y eso es, como mínimo, académicamente incorrecto.

No obstante, no se me ocurre ningún inconveniente no formal para desestimar esta sintaxis, ya que ninguna implementación de la interfaz lo haría de otra forma...

En cualquier caso, me parece una técnica cuanto menos peligrosa, ya que en un mal uso de ella se podrían cometer graves errores de diseño.

Para acabar, se me ocurren un par de preguntas para acabar de definir mi posición:
1) ¿En freya se pueden sobreescribir los métodos de lectura? entiendo que sí pero con esa sintaxis tan particular...

2) En caso que se pudiese dar la implementación en la interfaz, tal y como propones, se podria sobreescribir ésta en la clase?

miércoles, enero 02, 2008 1:34:00 a. m.  
Blogger Ian Marteens said...

y eso es, como mínimo, académicamente incorrecto.

;) Exacto... lo que no quiere decir que sea malo. El efecto anterior se logra en C# con los métodos de extensión, excepto que hay que usar un identificador adicional. Los métodos de extensión son más generales, claro, pero si este patrón de simular una implementación mediante extensiones se usa lo suficiente, puede merecer la pena permitir mi abreviatura.

Como curiosidad "formal" (no me pasaría por la cabeza probarlo), observa que este recurso se puede adaptar para usar las interfaces como "mixins". Sólo habría que introducir un modificador de acceso asociado a la implementación de una interfaz, y cada clase podría descomponerse en varios "mixins" de este tipo, y quizás algo de pegamento para unir los trozos. Naturalmente, la complicación de este modelo sería excesiva para que fuese práctica.

miércoles, enero 02, 2008 2:08:00 a. m.  
Blogger Ian Marteens said...

¿En freya se pueden sobreescribir los métodos de lectura?

Sí. El "override" va en la declaración, y si implementas la declaración directamente (estilo C#/Java/Eiffel), simplemente se trata de repetir el "override" en cada método de acceso. Eso sí, al igual que en C# hay que redefinir ambos métodos.

En caso que se pudiese dar la implementación en la interfaz, tal y como propones, se podria sobreescribir ésta en la clase?

No, porque la implementación sería mediante un método de extensión, que son estáticos. Es verdad que ahí falla la uniformidad del recurso: la decisión de implementarlo tendría que basarse en la conveniencia práctica de tal "abreviatura".

miércoles, enero 02, 2008 2:12:00 a. m.  
Blogger PabloNetrix said...

Joder, ¿¿pero qué demonios hacéis escribiendo posts un DOS DE ENERO A LAS DOS DE LA MAÑANA...

...Pudiendo haberlo hecho a las 00:02 del día 1?? Hmmm me estais perdiendo facultades! xDDD

Feliz año, y muchos éxitos personales y profesionales. :)


Saludos

miércoles, enero 02, 2008 12:35:00 p. m.  
Blogger Ian Marteens said...

qué demonios hacéis

Insomnio. Escribir me altera los horarios, y en estos días, estoy escribiendo bastante.

:) Feliz año.

miércoles, enero 02, 2008 5:20:00 p. m.  
Blogger Pablo Grisafi said...

El exceso de formalidad academica es malo para un leguaje...y en realidad, los mixines son muy útiles, en particular para hacer frameworks y cosas de "alto nivel", no tanto en una aplicación de negocios. Es la alternativa más razonable a la herencia múltiple...bah, tengo dias que pienso eso. Pero desde que leí sobre ellos en ruby, los quiero en c#!
Y hasta hay quien dice que habría que usar solamente interfaces y mixins, es decir, no usar nunca herencia...

miércoles, febrero 06, 2008 8:11:00 p. m.  

Publicar un comentario

<< Home