4 votes

Attribuer une méthode d'une classe à une instance d'une autre classe

Comme le titre l'indique, j'aimerais assigner toutes les méthodes d'instance définies sur une classe à une autre. Je sais que je peux obtenir une liste des méthodes que je veux copier à partir de ClassA a ClassB comme ceci :

ClassA.instance_methods(false)

Et je pense que je peux les définir sur ClassB comme ceci :

ClassA.instance_methods(false).each do |method_name|
  ClassB.method_define(method_name, [body here??])
end

Existe-t-il un moyen d'obtenir le corps de la méthode correspondante et, le cas échéant, cette méthode fonctionnera-t-elle ? Si ce n'est pas le cas, existe-t-il même un moyen de le faire ?

10voto

Boris Stitnicky Points 5409

D'autres vous ont déjà dit de créer une sous-classe. Mais pour répondre à votre question littérale, nous devrions nous occuper de UnboundMethod objets :

class Object
  def kokot; 'kokot' end
end

o = Object.new
o.kokot
#=> kokot

3.kokot
#=> kokot

Jusqu'à présent, tout va bien. Redéfinissons maintenant kokot méthode sur Numeric :

class Numeric
  def kokot; 'pica' end
end

o.kokot
#=> kokot
3.kokot
#=> pica

Mais que se passe-t-il si nous décidons que les nouveaux kokot est excellente pour les nombres, mais les nombres complexes devraient continuer à utiliser l'ancienne méthode kokot méthode. Nous pouvons procéder de la manière suivante :

um = Object.instance_method :kokot
#=> #<UnboundMethod: Object#kokot>
Complex( 2, 3 ).kokot # gives the redefined kokot method
#=> pica
Complex.module_exec { define_method :kokot, um }
# Now we've just bound the old kokot to Complex
Complex( 2, 3 ).kokot
#=> kokot

En bref, il existe un moyen de "copier et coller" des méthodes entre classes apparentées. La cible doit être une sous-classe de la méthode source non liée. Méthode #source_location indique le fichier et la ligne où #kokot a été défini :

um.source_location
#=> ["(irb)", 2]

Pour les méthodes intégrées, #source_location retours nil . Dans Ruby 2.0, RubyVM a une méthode #disassemble :

RubyVM::InstructionSequence.disassemble( um )
#=> ( program listing goes here )

Dans tous les cas, le bytecode Ruby n'est pas très beau à regarder. Pour en revenir à vos besoins initiaux, même pas #define_method o UnboundMethod#bind peut lier des méthodes à des objets incompatibles. Il n'est pas possible de contourner ce problème par des astuces telles que la redéfinition de l'objet #kind_of? il faudrait tricher avec la fonction CLASS_OF() dans le code natif...

Parmi les pierres précieuses disponibles, Sourcify , RubyParser y Sorcier sont intéressantes. (Merci, @Casper.) En les utilisant, on pourrait théoriquement transplanter du code entre des objets incompatibles par l'intermédiaire de #eval -ling méthode extraite source. Même si elle est longue, cette technique ne permet pas un véritable transfert de méthode, car elle échoue lorsque la source n'est pas disponible au moment de l'exécution (p. ex. source auto-modifiante).

3voto

John Naegle Points 2180

Il semble que ce que vous voulez, ce sont des mélanges :

Tiré de http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html

module Debug
  def whoAmI?
    "#{self.type.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.whoAmI?  »   "Phonograph (#537766170): West End Blues"
et.whoAmI?  »   "EightTrack (#537765860): Surrealistic Pillow"

1voto

Jim Gay Points 1329

En ruby 2.0, il est possible d'utiliser des modules. Matz explicitement a interdit ce comportement aux classes .

Mais vous pouvez utiliser les méthodes d'instance des modules.

ModuleA.instance_methods(false).each do |name|
  meth = ModuleA.instance_method(name)
  ClassB.send(:define_method, name, meth)
end

define_method est une méthode privée, c'est pourquoi vous utilisez send ici.

Mais pourquoi faire cela ? Il suffit d'inclure le module.

Si vous souhaitez simplement appliquer un comportement à un objet, vous pouvez délier une méthode de n'importe quel module et la lier à n'importe quel objet.

ModuleA.instance_method(:something).bind(some_object).call(args)

Si c'est ce que vous voulez, jetez un coup d'œil à casting , a qui ajoute une commodité à la délégation ainsi que l'ajout de méthodes à un objet uniquement pour la durée de vie d'un bloc.

0voto

sawa Points 62592

Dans ce cas, classB devrait hériter classA .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X