Votre question mise à jour semble assez différente maintenant. Si je vous comprends bien, vous voulez vous accrocher à l'allocation d'objets et à l'initialisation, ce qui n'a absolument rien à voir avec les métaclasses. (Mais n'écrit toujours pas ce que vous voulez réellement faire, donc je pourrais toujours être éteint.)
Dans certains langages orientés objet, les objets sont créés par les constructeurs. Cependant, Ruby n'a pas de constructeur. Les constructeurs ne sont que des méthodes d'usine (avec des restrictions stupides); il n'y a aucune raison de les avoir dans un langage bien conçu, si vous pouvez simplement utiliser une méthode d'usine (plus puissante) à la place.
construction d'objets Ruby fonctionne comme ceci: la construction de l'objet est divisé en deux phases, allocation et initialisation. L'allocation est effectuée par une méthode de classe publique appelée allocate
, qui est définie comme une méthode d'instance de la classe Class
et est généralement jamais remplacée par. (En fait, je ne pense pas que vous pouvez le remplacer.Il alloue juste l'espace mémoire pour l'objet et met en place quelques pointeurs, cependant, l'objet n'est pas vraiment utilisable à ce stade.
C'est là qu'intervient l'initialiseur: il s'agit d'une méthode d'instance appelée initialize
, qui définit l'état interne de l'objet et l'amène dans un état cohérent et entièrement défini pouvant être utilisé par d'autres objets.
Ainsi, afin de créer entièrement un nouvel objet, ce que vous devez faire est la suivante:
x = X.allocate
x.initialize
[Note:. Programmeurs Objective-C peuvent reconnaître]
Cependant, parce qu'il est trop facile à oublier d'appeler initialize
et en règle générale un objet devrait être entièrement valide après la construction, il y a une méthode usine commode appelée Class#new
, qui fait tout ce qui fonctionne pour vous et ressemble à ceci:
class Class
def new(*args, &block)
obj = allocate
obj.initialize(*args, &block)
return obj
end
end
[Note: En fait, initialize
est privé, donc la réflexion doit être utilisé pour contourner les restrictions d'accès comme celui-ci: obj.send(:initialize, *args, &block)
]
Ce, par ailleurs, est la raison pour laquelle de construire un objet que vous appel une méthode de classe publique Foo.new
mais mettre en œuvre une méthode d'instance privée Foo#initialize
, qui semble trébucher beaucoup de nouveaux arrivants.
Cependant, aucun n'est cuit dans le langage. Le fait que la méthode d'usine primaire pour une classe est généralement appelé new
est juste une convention (et parfois je souhaite qu'il était différent, car il ressemble à des constructeurs en Java, mais est complètement différent). Dans d'autres langues, le constructeur doit avoir un nom spécifique. En Java, il doit avoir le même nom que la classe, ce qui signifie que a) il ne peut y avoir qu'un seul constructeur et b) les classes anonymes ne peuvent pas avoir de constructeur car elles n'ont pas de noms. En Python, la méthode d'usine doit être appelée __new__
, ce qui signifie encore une fois qu'il peut y en avoir un seul. (Dans Java et Python, vous pouvez bien sûr avoir différentes méthodes d'usine, mais les appeler est différent d'appeler la valeur par défaut, tandis que dans Ruby (et Smalltalk d'où ce modèle est originaire), il semble identique.)
In Ruby, il peut y avoir autant de méthodes d'usine que vous le souhaitez, avec n'importe quel nom que vous aimez, et une méthode d'usine peut avoir plusieurs noms différents. (Pour les classes de collecte, par exemple, la méthode de fabrication est souvent aliasé à []
, qui vous permet d'écrire List[1, 2, 3]
au lieu de List.new(1, 2, 3)
qui se termine à la recherche plus comme un tableau, soulignant ainsi la collection ish nature des listes.)
En bref:
- la méthode d'usine normalisée est
Foo.new
, mais il peut être quelque chose
Foo.new
appels allocate
à allouer de la mémoire pour un objet vide foo
Foo.new
appelle alors foo.initialize
, c'est-à-direla méthode d'instance Foo#initialize
- les trois personnes ne sont que des méthodes comme les autres, que vous pouvez annuler la définition, redéfinir, override, wrap, alias et ainsi de suite
bien
- , à l'exception
allocate
qui doit allouer de la mémoire à l'intérieur du moteur d'exécution Ruby qui vous ne peut pas vraiment faire de Ruby
en Python, __new__
correspond à peu près à la fois new
et allocate
en Ruby, et __init__
exactement correspond à initialize
en Ruby. La principale différence est que dans Ruby, new
appelle initialize
alors qu'en Python, le runtime appelle automatiquement __init__
après __new__
.
Par exemple, voici une classe qui permet seulement un maximum de 2 instances créées:
class Foo
def self.new(*args, &block)
@instances ||= 0
raise 'Too many instances!' if @instances >= 2
obj = allocate
obj.send(:initialize, *args, &block)
@instances += 1
return obj
end
attr_reader :name
def initialize(name)
@name = name
end
end
one = Foo.new('#1')
two = Foo.new('#2')
puts two.name # => #2
three = Foo.new('#3') # => RuntimeError: Too many instances!
Hmm ... je peux être manque encore quelque chose, mais après avoir lu quelques liens, il commence à sonner comme Python La métaclasse et la métaclasse de Ruby sont différentes. J'espère avoir l'occasion de faire plus de recherches et de poster sur ce que j'ai appris. C'est bien sûr à moins que quelqu'un le sache. Liens: [Katz sur Métaclasses] (http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/), [et Ruby Internes métaclasse ] (http://www.klankboomklang.com/2007/10/05/the-metaclass/) –