Xavier Nayrac

Rubyiste accro au TDD, serial blogger, apprenti data scientist, heureux utilisateur de Vim, accordéoniste.
Si vous vous sentez particulièrement généreux, suivez moi sur Twitter.

Quel est l'intérêt de cette syntaxe ?

| Comments

Niveau : intermédiaire

En googlant sur ruby design pattern factory je suis tombé sur un post intéressant. Non, je ne vais pas vous parler de design pattern dans cet article. L’exemple qui m’a plus particulierment intrigué est le suivant:

Exemple original
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class AdjacencyMatrix
  class << self
    def undirected(data)
      new(data)
    end

    def directed(data)
      new(data,true)
    end

    private :new
  end

  def initialize(data, directed=false)
    #...  
  end
end

undirected_matrix = AdjacencyMatrix.undirected(data)
directed_matrix   = AdjacencyMatrix.directed(data)

Pourquoi cet exemple m’a intrigué ? Pas parce qu’il parle de matrice, je vous rassure. Je ne comprends pas grand chose aux matrices, et je n’ai jamais entendu parler d’«adjacency matrix». Si cet exemple m’intrigue, c’est parce que je ne comprends pas, à priori, l’intérêt du class << self. Il me semble que je peux réécrire ça sans class << self et que le résultat serait le même.

Alors allons y. Voilà le nouveau bout de code qui fait la même chose que le précédent:

Seconde version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class AdjacencyMatrix
  def self.undirected(data)
    new(data)
  end

  def self.directed(data)
    new(data,true)
  end

  private_class_method :new

  def initialize(data, directed=false)
    #...  
  end
end

undirected_matrix = AdjacencyMatrix.undirected(data)
directed_matrix   = AdjacencyMatrix.directed(data)

L’API et le résultat sont identiques. Alors c’est pas nouveau, avec Ruby il y a toujours deux (ou trois, ou plus) manières différentes de faire la même chose. Mais là, je me demande plus particulièrment si il y a un intérêt à utiliser la syntaxe du premier exemple. Et je ne vois pas. Du coup il me vient une seconde question : entre ces deux exemples, quel est le meilleur code ? Et part meilleur, j’entends bien sûr le plus lisible.
Sans vous faire attendre plus longtemps, je pense que le second exemple possède le code le plus lisible. Pourquoi ? Parce qu’il est plus simple ? Non, parce que ça n’est pas forcement très pertinent dans ce cas précis. Même si je pense que 9 fois sur 10 un code plus simple est un code plus maintenable, je dois reconnaitre que quelqu’un d’habitué à manipuler du code Ruby comprendra aussi facilement et rapidement les deux syntaxes précédentes.

En fait, si le second exemple me parait plus lisible, c’est parce qu’il possède un niveau d’indentation du code en moins. Plus on est proche de la marge gauche, et plus le code se lit aisément. C’est pas toujours évident à voir sur des exemples aussi courts, c’est vrai. Mais quand on lit du code, comme celui de l’exemple 1, étalé sur une centaine de lignes ou plus, il est facile de se perdre et/ou d’oublier si on a affaire à une méthode de classe ou à une méthode d’objet. Un code devrait pouvoir se lire comme une histoire. Pour pousser la logique, je voudrais lire du code aussi facilement et naturellement que je lis un livre. Et dans un livre, les lignes débutent à la marge gauche. Voilà pourquoi je préfère le code du second exemple.

Quel est l’intérêt de class << self ?

Quoi qu’il en soit, je n’ai toujours pas de réponse à ma première question: «quel est l’intérêt de class << self» dans cet exemple précis. Il est possible que dans une ancienne version de Ruby, il n’y avait pas moyen de faire autrement et qu’on ai gardé l’habitude ? Peut-être qu’il n’y a aucune réponse ? Je vais donc aller faire un tour du coté de la divinité StackOverflow pour tenter d’y voir plus clair. Je vous tient au courant dans un futur article si je trouve quelque chose.

À demain.

Articles connexes

Commentaires