lunes, 10 de diciembre de 2012

Mixins, Mixins y más Mixins...


En el post anterior vimos la tecnica de Mixins en Scala, pero no solo Scala tiene esta técnica. Muchos lenguajes denominados modernos utilizan Mixins un ejemplo claro es Ruby. En Ruby definimos módulos y podemos importar estos módulos en nuestras clases.


module Debug
   def who_am_i?
      "#{self.class.name} (\##{self.id}): #{self.to_s}"
   end
end

class Phonograph
  include Debug
  # ...
end

class EightTrack
  include Debug
  # ...
end

ph = Phonograph.new("West End Blues")
et = EightTrack.new("Surrealistic Pillow")

ph.who_am_i? #Phonograph (#935520): West End Blues
et.who_am_i?  #EightTrack (#935500): Surrealistic Pillow

El caso de groovy resuelve el mixins con anotaciones, es como una fusión de java y ruby.


class Dog {
  def talk() {
    "Woof, woof"
  }
}


@Mixin(Dog)
class Talk {
  // other stuff here
}

Y listo! Pero a la vez groovy permite hacer mixins con instancias no solo con clases de la siguiente manera:

someoneElsesClass.mixin(Dog)

Como es dinámicamente tipado, no hay problema si hacemos:

someoneElsesClass.talk()

Si recuerdan el post anterior en Scala si queríamos hacer esto usábamos with en el constructor tipando de esta manera al objeto:


val snowy = new Cat("Snowy" ) with Friend
val friend : Friend = snowy
friend.listen

El método mixin de Groovy tiene la ventaja que se puede utilizar en cualquier momento, no como la palabra reservada de Scala with que solo se puede utilizar en el constructor.

Por ejemplo Dart implementa el mixins muy similar a scala, es decir tiene trait que pueden ser agregados a las instancias o a las clases. Lo que agrega es que también tiene interfaces, veamos un ejemplo:


interface Shape {
   num perimeter();
}

trait Rectangle implements Shape {
   final num height, width;
 
   Rectangle(num this.height, num this.width); // Compact constructor syntax.
 
   num perimeter() => 2*height + 2*width; // Short function syntax.
}

class Square extends Rectangle {
   Square(num size) : super(size, size);
}

En Ceylon existen las interfaces que al igual que Scala pueden contener código; es decir una interface de Ceylon es igual a un trait de Scala:

shared interface Writer {
    shared formal Formatter formatter;
     
    shared formal void write(String string);
     
    shared void writeLine(String string) {
        write(string);
        write(process.newLine);
    }
     
    shared void writeFormattedLine(String formatString, Object... args) {
        writeLine( formatter.format(formatString, args) );
    }
     
}

shared class ConsoleWriter()
        satisfies Writer {
     
    formatter = StringFormatter();
     
    shared actual void write(String string) {
        writeLine(string);
    }
     
}


Uff, como podemos ver la idea es la misma cambia un poco la forma de implementarla, cambia un poco más si el lenguaje es dinámicamente tipado o estáticamente tipado.

Mixins es una característica de los lenguajes modernos que esta bueno ir aprendiendo.

Que implementación les gusto más?

2 comentarios:

  1. Hay una diferencia sutil pero importante entre los traits de Scala y las interfaces en Ceylon: en Ceylon no puedes guardar estado en las interfaces; es decir, no puedes tener atributos concretos (ni mutables ni inmutables). Solamente puedes definir atributos como "formal", para que los implemente la clase que satisfaga dicha interfaz. La única manera de tener atributos concretos en una interfaz es definirlos con getter y opcionalmente con setter, pero atributos simples no lo permite. Esto fue una decisión de diseño para evitar problemas que surgen de heredar estado similar de dos interfaces dispares. Para mayor información ver esta sección de la página oficial: http://ceylon-lang.org/documentation/1.0/tour/inheritance/#interfaces_and_mixin_inheritance

    ResponderBorrar