lundi, mars 31, 2008

Experimentations avec les Mixins / Modules et les méthodes statiques (self)

On m'a posé une question aujourd'hui: "Comment définir une méthode statique dans un module et l'inclure dans une classe ?". Simple non ?!
Justement j'avais eu le même problème quand j'avais fait mon projet "natural_sort"
Spontanément j'ai essayé:

module Carnivore
  def self.manger(oiseau)
    puts "slurp! #{oiseau}"
  end
end

class GrosMinet
  include Carnivore
end

Carnivore.manger "titi" # ok ca foncionnne
GrosMinet.manger "titi" # undefined method `manger' for GrosMinet:Class (NoMethodError) WTF ?!
Malheureusement ça ne fonctionne pas :(

Dans NaturalSort, j'ai contourné le problème:

module NaturalSort
  def self.naturalsort(object)
    ...
  end
  def natural_sort
    NaturalSort::naturalsort(to_a)
  end
end
Je suis tombé sur cet article: "Ruby Mixin Tutorial" et j'ai essayé différentes combinaisons, il n'y en pas une qui soit satifaisante dans tous les cas...
Ici vous pourrez voir les principales étapes jusqu'à la version finale, celle qui me satisfait le plus.
Etape n°1: classe statique dans le module uniquement

module Carnivore
  def self.manger(oiseau)
    puts "slurp! #{oiseau}"
  end
end

Carnivore.manger "titi"
Etape n°2: méthode d'instance de classe (l'utilisation classique du module par excellence):

module Carnivore
  def manger(oiseau)
    puts "slurp! #{oiseau}"
  end
end

class GrosMinet
  include Carnivore
end

gros_minet = GrosMinet.new 
gros_minet.manger "titi"
Version finale. 2 grosses subtilités:
  • il n'y a pas de 'self' dans la définition de la méthode 'manger'
  • On utilise 'extends' au lieu de 'include'

module Carnivore
  def manger(oiseau)
    puts "slurp! #{oiseau}"
  end
end

module Chat
  def miauler
    puts "miaou..."
  end
end

class GrosMinet
  include Chat
  # include Carnivore # necessaire à gros_minet.manger("titi")
  extend Carnivore
end

GrosMinet.manger "titi"

gros_minet = GrosMinet.new 
gros_minet.miauler
# gros_minet.manger "titi" # necessite le include Carnivore pour fonctionner

Conclusion: Ca fonctionne et le code est court mais je trouve la syntax un peut étrange, je ne comprend pas trop pourquoi le 'include' n'inclus pas les méthodes 'self' ? il y a quelquechose qui m'échappe pour l'instant...

Du coup je pense que c'est une bonne pratique de ne pas utiliser de méthodes préfixé par 'self' dans le modules et que faire MonModule.ma_methode n'est peut-être pas très orienté objet... après tout les modules ne sont pas vraiment des objets et ne devraient pas être utilisés seuls ? Qu'est ce que vous en pensez ?


Technorati tags:

1 commentaire:

Pouype a dit…

Je crois surtout qu'il ne faut pas chercher à utiliser les méthodes statique comme dans le monde Java par exemple.

Dans ruby, l'utilisation de méthode statique dans un module ne me choque pas, c'est juste qu'il faut avoir à l'esprit que c'est une méthode propre au module (puisque statique) et donc non partagé par l'instance d'un objet l'incluant, mais disponible uniquement par l'instance du Module directement.

Enfin bref. Si c'est possible, il y a surement un interêt :)