Dans Ruby, je comprends que les fonctions du module peuvent être mises à disposition sans mélange dans le module en utilisant module_function
comme indiqué ici. Je peux voir comment cela est utile pour que vous puissiez utiliser la fonction sans mélanger dans le module.
module MyModule
def do_something
puts "hello world"
end
module_function :do_something
end
Ma question est cependant de savoir pourquoi vous voudrez peut-être que la fonction soit définie de ces deux manières.
Pourquoi ne pas simplement
def MyModule.do_something
OR
def do_something
Dans quel type de cas serait-il utile d'avoir la fonction disponible pour être mélangée ou utilisée comme méthode statique?
Pensez à énumérable .
Ceci est l'exemple parfait du moment où vous devez l'inclure dans un module. Si votre classe définit #each
, vous obtenez beaucoup de bonté simplement en incluant un module (#map
, #select
, etc.). C'est le seul cas où j'utilise des modules en tant que mixins - lorsque le module fournit des fonctionnalités en termes de quelques méthodes, définies dans la classe dans laquelle vous l'incluez. Je peux affirmer que ce devrait être le seul cas en général.
Quant à la définition de méthodes "statiques", une meilleure approche serait:
module MyModule
def self.do_something
end
end
Vous n'avez pas vraiment besoin d'appeler #module_function
. Je pense que c'est juste des trucs hérités bizarres.
Vous pouvez même faire ceci:
module MyModule
extend self
def do_something
end
end
... mais cela ne fonctionnera pas bien si vous souhaitez également inclure le module quelque part. Je suggère de l'éviter jusqu'à ce que vous appreniez les subtilités de la méta-programmation Ruby.
Enfin, si vous faites juste:
def do_something
end
... cela ne se terminera pas comme une fonction globale, mais comme une méthode privée sur Object
(il n'y a pas de fonctions dans Ruby, juste des méthodes). Il y a deux inconvénients. Tout d'abord, vous n'avez pas d'espaces de noms - si vous définissez une autre fonction avec le même nom, c'est celle qui sera évaluée plus tard que vous obtenez. Deuxièmement, si vous avez mis en œuvre des fonctionnalités en termes de #method_missing
, avoir une méthode privée dans Object
l'occultera. Et enfin, le patch de singe Object
est juste une mauvaise affaire :)
ÉDITER:
module_function
peut être utilisé d'une manière similaire à private
:
module Something
def foo
puts 'foo'
end
module_function
def bar
puts 'bar'
end
end
De cette façon, vous pouvez appeler Something.bar
, mais pas Something.foo
. Si vous définissez d'autres méthodes après cet appel à module_function
, ils seraient également disponibles sans se mélanger.
Je n'aime pas ça pour deux raisons, cependant. Premièrement, les modules qui sont à la fois mixés et qui ont des méthodes "statiques" semblent un peu douteux. Il peut y avoir des cas valides, mais ce ne sera pas si fréquent. Comme je l'ai dit, je préfère utiliser un module comme espace de noms ou le mélanger, mais pas les deux.
Deuxièmement, dans cet exemple, bar
serait également disponible pour les classes/modules qui se mélangent dans Something
. Je ne sais pas quand cela est souhaitable, car soit la méthode utilise self
et elle doit être mélangée, soit pas et alors elle n'a pas besoin d'être mélangée.
Je pense qu'en utilisant module_function
sans passer le nom de la méthode est utilisé plus souvent qu'avec. Il en va de même pour private
et protected
.
C'est un bon moyen pour une bibliothèque Ruby d'offrir des fonctionnalités qui n'utilisent pas (beaucoup) d'état interne. Donc si vous (par exemple) voulez offrir une fonction sin
et ne pas ' Si vous voulez polluer l'espace de noms "global" (Object
), vous pouvez le définir comme méthode de classe sous une constante (Math
).
Cependant, un développeur d'applications qui souhaite écrire une application mathématique peut avoir besoin de sin
toutes les deux lignes. Si la méthode est également une méthode d'instance, elle peut simplement inclure le Math
(ou My::Awesome::Nested::Library
) et peut désormais appeler directement sin
(exemple stdlib).
Il s'agit vraiment de rendre une bibliothèque plus confortable pour ses utilisateurs. Ils peuvent se choisir eux-mêmes, s'ils souhaitent que les fonctionnalités de votre bibliothèque soient de niveau supérieur.
Soit dit en passant, vous pouvez obtenir une fonctionnalité similaire comme module_function
en utilisant: extend self
(dans la première ligne du module). À mon avis, cela semble mieux et rend les choses un peu plus claires à comprendre.
Mise à jour: Plus d'informations de fond dans cet article de blog .
Si vous souhaitez consulter un exemple de travail, consultez la gemme chronique:
https://github.com/mojombo/chronic/blob/master/lib/chronic/handlers.rb
et Handlers est inclus dans la classe Parser ici:
https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb
Il utilise module_function pour envoyer les méthodes des gestionnaires à des instances spécifiques de gestionnaire en utilisant la méthode d'invocation de cette instance.