Au lieu de supporter la surcharge de méthode, Ruby écrase les méthodes existantes. Quelqu'un peut-il expliquer pourquoi le langage a été conçu de cette façon?
La surcharge de méthodes peut être obtenue en déclarant deux méthodes avec le même nom et des signatures différentes. Ces différentes signatures peuvent être soit,
method(int a, int b) vs method(String a, String b)
method(a) vs method(a, b)
La première méthode ne permet pas de surcharger une méthode car il n’ya pas de déclaration de type de données en Ruby ( dynamic typed language ). Donc, la seule façon de définir la méthode ci-dessus est def(a,b)
Avec la deuxième option, il peut sembler que nous pouvons parvenir à une surcharge de méthode, mais nous ne pouvons pas. Disons que j'ai deux méthodes avec un nombre différent d'arguments,
def method(a); end;
def method(a, b = true); end; # second argument has a default value
method(10)
# Now the method call can match the first one as well as the second one,
# so here is the problem.
Ainsi, Ruby doit conserver une méthode dans la chaîne de recherche de méthode avec un nom unique.
"Surcharger" est un terme qui n'a tout simplement pas de sens en Ruby. C'est fondamentalement un synonyme de "dispatch statique à base d'arguments", mais Ruby ne ---) a (= dispatch du tout. Donc, la raison pour laquelle Ruby ne prend pas en charge la répartition statique basée sur les arguments, c'est parce qu'elle ne prend pas en charge la répartition statique, point. Il ne supporte pas l'envoi statique de tout type, qu'il soit basé sur des arguments ou autrement.
Maintenant, si not vous posez précisément des questions sur la surcharge, mais peut-être sur dynamique une répartition basée sur des arguments, la réponse est: car Matz ne l’a pas mis en œuvre. Parce que personne d'autre n'a pris la peine de le proposer. Parce que personne d'autre n'a pris la peine de l'appliquer.
En général, la répartition dynamique basée sur les arguments dans une langue avec des arguments facultatifs et des listes d'arguments de longueur variable est très difficile à obtenir correctement et même plus difficile à le rendre compréhensible. Même dans les langages avec une répartition basée sur des arguments statique et sans arguments optionnels (comme Java, par exemple), il est parfois presque impossible de dire pour un simple mortel, qui surcharge est va être choisi.
En C #, vous pouvez réellement encoder n'importe lequel problème de 3-SAT en résolution de surcharge, ce qui signifie que la résolution de surcharge en C # est NP-difficile.
Essayez maintenant cela avec dynamique dispatch, où vous avez la dimension temporelle supplémentaire à garder dans votre tête.
Il existe des langages qui envoient de manière dynamique en fonction de tous les arguments d'une procédure, par opposition aux langages orientés objet, qui envoient uniquement sur l'argument zeroth self
"caché". LISP commun, par exemple, distribue sur les types dynamiques et même les valeurs dynamiques de tous les arguments. Clojure distribue une fonction arbitraire de tous les arguments (BTW est extrêmement cool et extrêmement puissant).
Mais je ne connais pas de langage OO avec une répartition dynamique basée sur des arguments. Martin Odersky a déclaré qu'il pourrait envisager d'ajouter une dépêche argumentée à Scala, mais seulement s'il peut supprimer la surcharge en même temps et être rétrogradé -compatible à la fois avec le code Scala existant qui utilise la surcharge et compatible avec Java (il a notamment mentionné Swing et AWT qui jouent des tours extrêmement complexes exerçant à peu près tous les cas de coins sombres de Java règles de surcharge complexes). J'ai moi-même eu quelques idées sur l'ajout d'une répartition basée sur les arguments à Ruby, mais je n'ai jamais trouvé comment le faire de manière rétro-compatible.
Je suppose que vous recherchez la possibilité de le faire:
def my_method(arg1)
..
end
def my_method(arg1, arg2)
..
end
Ruby soutient cela d'une manière différente:
def my_method(*args)
if args.length == 1
#method 1
else
#method 2
end
end
Un modèle courant consiste également à transmettre les options sous forme de hachage:
def my_method(options)
if options[:arg1] and options[:arg2]
#method 2
elsif options[:arg1]
#method 1
end
end
my_method arg1: 'hello', arg2: 'world'
J'espère que cela pourra aider
La surcharge de méthode est logique dans un langage avec typage statique, où vous pouvez distinguer différents types d'arguments
f(1)
f('foo')
f(true)
ainsi qu'entre différents nombres d'arguments
f(1)
f(1, 'foo')
f(1, 'foo', true)
La première distinction n'existe pas en Ruby. Ruby utilise la frappe dynamique ou "frappe de canard". La deuxième distinction peut être gérée par des arguments par défaut ou en utilisant des arguments:
def f(n, s = 'foo', flux_compensator = true)
...
end
def f(*args)
case args.size
when
...
when 2
...
when 3
...
end
end
Cela ne répond pas à la question de savoir pourquoi Ruby n'a pas de surcharge de méthodes, mais que des bibliothèques tierces peuvent le fournir.
La bibliothèque contracts.Ruby permet la surcharge. Exemple adapté du tutoriel:
class Factorial
include Contracts
Contract 1 => 1
def fact(x)
x
end
Contract Num => Num
def fact(x)
x * fact(x - 1)
end
end
# try it out
Factorial.new.fact(5) # => 120
Notez que ceci est en réalité plus puissant que la surcharge de Java, car vous pouvez spécifier des valeurs à comparer (par exemple, 1
), pas simplement des types.
Vous constaterez une diminution des performances en utilisant ceci cependant; vous devrez exécuter des points de repère pour décider combien vous pouvez tolérer.
Je fais souvent la structure suivante:
def method(param)
case param
when String
method_for_String(param)
when Type1
method_for_Type1(param)
...
else
#default implementation
end
end
Ceci permet à l'utilisateur de l'objet d'utiliser le nom_méthode propre et clair: méthode Mais s'il souhaite optimiser l'exécution, il peut directement appeler la méthode correcte.
En outre, cela rend vos tests plus clairs et meilleurs.
il y a déjà d'excellentes réponses sur le côté pourquoi de la question. Cependant, si vous recherchez d'autres solutions, utilisez la commande fonctionnaliste gemme inspirée d'Elixir la recherche de motifs .
classe Foo include Functional :: PatternMatching ## Constructor Over loading defn (: initialize) {@name = 'baz'} defn (: initialize, _) {| nom | @nom = nom.to_s} ## Surcharge de méthode defn (: greet,: male) { met "Bonjour, monsieur!" } defn (: greet,: female) { met "Bonjour, madame!" } fin foo = Foo.new ou Foo.new ('Bar') foo.greet (: male) => "Bonjour, monsieur!" foo.greet (: female) => "Bonjour, madame!"