Ce que je veux c'est:
obj = Foo.new(0) # => nil or false
Cela ne fonctionne pas:
class Foo
def initialize(val)
return nil if val == 0
end
end
Je sais qu'en C/C++/Java/C #, nous ne pouvons pas retourner une valeur dans un constructeur.
Mais je me demande si c'est possible en Ruby.
Dans Ruby, quelle est la relation entre '
new
' et 'initialize
'?
new
appelle généralement initialize
. L'implémentation par défaut de new
est quelque chose comme:
class Class
def new(*args, &block)
obj = allocate
obj.initialize(*args, &block)
# actually, this is obj.send(:initialize, …) because initialize is private
obj
end
end
Mais vous pouvez, bien sûr, le remplacer pour faire tout ce que vous voulez.
Comment retourner nil lors de l'initialisation?
Ce que je veux c'est:
obj = Foo.new(0) # => nil or false
Cela ne fonctionne pas:
class Foo def initialize(val) return nil if val == 0 end end
Je sais qu'en C/C++/Java/C #, nous ne pouvons pas retourner une valeur dans un constructeur.
Mais je me demande si c'est possible en Ruby.
Il n'y a pas de constructeur en Ruby. Dans Ruby, il n'y a que des méthodes et elles peuvent renvoyer des valeurs.
Le problème que vous voyez est simplement que vous souhaitez modifier la valeur de retour d'une méthode mais que vous remplacez une autre méthode. Si vous souhaitez modifier la valeur de retour de la méthode bar
, vous devez remplacer bar
, pas une autre méthode.
Si vous souhaitez modifier le comportement de Foo::new
, vous devez alors modifier Foo::new
:
class Foo
def self.new(val)
return nil if val.zero?
super
end
end
Notez, cependant, qu'il s'agit d'un très mauvaise idée, car il viole le contrat de new
, qui consiste à renvoyer une instance pleinement initialisée et pleinement fonctionnelle de la classe.
Il existe des différences importantes entre les deux méthodes.
new
est une méthode class, qui crée généralement une instance de la classe (cela traite des choses délicates comme l'allocation de mémoire dont Ruby vous protège de vous n'avez donc pas à vous salir trop).
Ensuite, initialize
, une méthode instance, indique à l'objet de définir son état interne en fonction des paramètres demandés.
Ces deux options peuvent être remplacées en fonction de ce que vous souhaitez. Par exemple, Foo.new
Peut réellement créer et renvoyer une instance de FooSubclass
si elle doit être suffisamment intelligente pour le faire.
Cependant, il est souvent préférable de déléguer des cas d'utilisation comme ceux-ci à d'autres méthodes de classe qui sont plus explicites sur ce qu'elles font, par exemple Foo.relating_to(bar)
. Briser les attentes des autres sur ce que des méthodes comme new
devraient faire va dérouter les gens plus que cela ne les aidera à long terme.
Par exemple, regardez l'implémentation de Singleton
, un module qui permet à une seule instance d'une classe particulière d'exister. Il rend la méthode new
privée et expose une méthode instance
qui retourne l'instance existante de l'objet ou appelle new
si elle n'a pas encore été créée.
Vous pouvez quelque chose comme ça:
class Foo
def self.init(val)
new(val) unless val == 0
end
def initialize(val)
#...
end
end
Exemple d'utilisation:
obj = Foo.init(0)
=> nil
obj = Foo.init(5)
=> #<Foo:0x00000002970a98>
Vouloir faire
class Foo
def initialize(val)
return nil if val == 0
end
end
rendrait le code incohérent.
Si tu avais
class Foo
def initialize(val)
return nil if val == 0
@val = val
@bar = 42
end
end
que voudriez-vous récupérer si vous faisiez Foo.new(1)
? Souhaitez-vous 42
(La valeur de retour pour Foo#initialize
), Ou un objet foo
? Si vous voulez un objet foo
pour Foo.new(1)
, alors pourquoi vous attendriez-vous à ce que return nil
Fasse en sorte que Foo.new(0)
renvoie zéro?