Dans Ruby, supposons que j'ai une classe Cela me permet de faire p> mais pas p> malgré le fait que Évidemment, je pourrais définir une méthode d'instance Je peux également le faire en définissant Que recommanderiez-vous? P> P> foo code> pour me permettre de cataloguer ma grande collection de foos. C'est une loi fondamentale de la nature que tous les foos sont verts et sphériques, j'ai donc défini des méthodes de classe comme suit:
my_foo code> est clairement vert. P>
couleur code> qui appelle
self.class.colour code>, mais qui devient manifeste si j'ai de nombreuses caractéristiques fondamentales. p>
méthodal_missing code> pour essayer la classe pour toute méthode manquante, mais je ne suis pas clair si c'est quelque chose que je devrais faire ou un piratage laids, Ou comment le faire en toute sécurité (surtout que je suis en fait sous Activerecord dans des rails, que je crois comprendre fait des trucs amusants intelligents avec la méthode_missing). P>
8 Réponses :
Vous pouvez utiliser un module:
module FooProperties def colour ; "green" ; end def is_spherical? ; true ; end end class Foo extend FooProperties include FooProperties end
Agréable. Cela peut-il gérer l'héritage? C'est-à-dire que si la chose et la barre héritent de FOO, et les choses sont toujours "lourdes", tandis que les barres sont toujours "légères"; Aurais-je besoin de munitions séparées et de modules de barproperties, ou je peux le rouler en bœufroperties en quelque sorte?
Cela va sonner comme un peu de flic, mais dans la pratique, il est rarement nécessaire de le faire, lorsque vous pouvez appeler FOO.Color tout aussi facilement. L'exception est si vous avez de nombreuses classes avec des méthodes de couleur définies. @Var pourrait être l'une des classes de plusieurs classes et vous souhaitez afficher la couleur, peu importe. P>
Quand c'est le cas, je vous demanderais d'où vous utilisez la méthode davantage - sur la classe ou sur le modèle? C'est presque toujours l'un ou l'autre et il n'y a rien de mal à en faire une méthode d'instance même si elle devrait être la même dans toutes les instances. P>
Dans l'événement rare, vous souhaitez que la méthode "appelable" par les deux, vous pouvez soit faire @ var.class.color (sans créer une méthode spéciale) ni créer une méthode spéciale comme suit: P>
de couleur def Self.class.color fin p>
J'éviterais certainement la solution Catch (Method_missing), car elle vous excuse de vraiment envisager l'utilisation de chaque méthode, et si elle appartient au niveau de la classe ou de l'instance. P>
Dans une perspective de conception, je dirais que, même si la réponse est la même pour tous les Je peux cependant voir certains cas où vous voudriez que ce comportement E.G. Lorsque vous avez En outre, vous êtes correct que ActiveRecord utilise une utilisation intensive de foos code>, couleur et sphérique? sont des propriétés des instances de
foo code> et comme telles doivent être définies comme des méthodes d'instance plutôt que des méthodes de classe. P>
barres code> dans votre système, qui sont également bleus et que vous êtes passé une classe quelque part dans votre code et que vous souhaitez savoir quelle couleur une instance sera avant d'appeler
nouveau < / code> sur la classe. P>
Method_missing Code> E.G. Pour les résultats dynamiques, si vous êtes tombé en panne de cet itinéraire, vous devez vous assurer que votre méthode_MISSING a appelé celui de la superclasse s'il a déterminé que le nom de la méthode n'était pas celui qu'il pourrait se charger de lui-même. P>
Vous avez absolument raison, ce sont des propriétés de l'instance plutôt que de la classe - mais vous avez cloué la situation dont j'ai besoin de cette fonction; Je dois prendre une décision dans mon code lorsque j'ai la classe dans la main, avant d'appeler nouveau.
Je pense que la meilleure façon de le faire serait d'utiliser La méthode de la matrice de Dwemthy .
Je vais le chercher et combler les détails, mais voici le squelette p>
edit strong> : Yay! Travailler! P> class Object
# class where singleton methods for an object are stored
def metaclass
class<<self;self;end
end
def metaclass_eval &block
metaclass.instance_eval &block
end
end
module Defaults
def self.included(klass, defaults = [])
klass.metaclass_eval do
define_method(:add_default) do |attr_name|
# first, define getters and setters for the instances
# i.e <class>.new.<attr_name> and <class>.new.<attr_name>=
attr_accessor attr_name
# open the class's class
metaclass_eval do
# now define our getter and setters for the class
# i.e. <class>.<attr_name> and <class>.<attr_name>=
attr_accessor attr_name
end
# add to our list of defaults
defaults << attr_name
end
define_method(:inherited) do |subclass|
# make sure any defaults added to the child are stored with the child
# not with the parent
Defaults.included( subclass, defaults.dup )
defaults.each do |attr_name|
# copy the parent's current default values
subclass.instance_variable_set "@#{attr_name}", self.send(attr_name)
end
end
end
klass.class_eval do
# define an initialize method that grabs the defaults from the class to
# set up the initial values for those attributes
define_method(:initialize) do
defaults.each do |attr_name|
instance_variable_set "@#{attr_name}", self.class.send(attr_name)
end
end
end
end
end
class Foo
include Defaults
add_default :color
# you can use the setter
# (without `self.` it would think `color` was a local variable,
# not an instance method)
self.color = "green"
add_default :is_spherical
# or the class instance variable directly
@is_spherical = true
end
Foo.color #=> "green"
foo1 = Foo.new
Foo.color = "blue"
Foo.color #=> "blue"
foo2 = Foo.new
foo1.color #=> "green"
foo2.color #=> "blue"
class Bar < Foo
add_defaults :texture
@texture = "rough"
# be sure to call the original initialize when overwriting it
alias :load_defaults :initialize
def initialize
load_defaults
@color = += " (default value)"
end
end
Bar.color #=> "blue"
Bar.texture #=> "rough"
Bar.new.color #=> "blue (default value)"
Bar.color = "red"
Bar.color #=> "red"
Foo.color #=> "blue"
Cela pourrait être ce que je recherche ... dans l'attente de la mise à jour détaillée.
@Chris: la mise à jour détaillée est terminée. Faites-moi savoir si vous rencontrez des problèmes avec cela.
Actuellement, la couleur n'est pas héritée (car elle est stockée dans une variable d'instance de la classe), mais vous pouvez la modifier de manière à ce que les valeurs de défaut actuelles soient héritées lorsqu'une classe est sous-classée en écrivant également une autre chose.
Fancy Stuff là-bas ... Chris, si vous souhaitez apprendre plus de métaprogrammation de choses comme celle-ci, je vous recommande des screencasts de Dave Thomas à PragProg.com. Ils ne sont que 5 $ et ça vaut vraiment la peine: PragProg .com / Screencasts / V-DTRubyom / ...
Prete magie ici. Je vais aller avec la solution de passthrough d'Aidan, mais je pense que je voudrais prendre le temps de comprendre cela aussi.
Vous pouvez définir une installation passthrough: Je n'aime généralement pas utiliser eval code>, mais il devrait être assez sûr, ici. P> p>
C'est aussi un concurrent très fort pour exactement ce dont j'ai besoin.
Celui-ci traitera avec beaucoup mieux la succession que l'autre réponse que j'ai postée.
Ouais. C'est simple, je comprends ce que ça fait, il gère l'héritage, et il arrive beaucoup ressembler à des déclarations de style rails (HAS_MANY, etc.)!
Le module transférable avec RUBY le fera bien:
#!/usr/bin/ruby1.8 require 'forwardable' class Foo extend Forwardable def self.color "green" end def_delegator self, :color def self.is_spherical? true end def_delegator self, :is_spherical? end p Foo.color # "green" p Foo.is_spherical? # true p Foo.new.color # "green" p Foo.new.is_spherical? # true
Légèrement plus court: déf_delegator Soi,: Couleur Code>
Notez que si vous donnez def_delegator code> un symbole pour son premier argument, il sera évalué que plus tard. Donc, si vous sous-classe
foo code> avec
foochild code>, et que vous souhaitez que les instances de FOOCHILD soient déléguées à code> foochild code> et non
foo code>, utilisation
DEF DÉLÉDATEUR: SOFT,: Couleur CODE> Pour garder
auto code> d'être évalué sous forme
foo code>. Cela semble vraiment être un eval:
def_delegator: "met" hi '; auto " code> aura le même effet mais imprime à la norme.
En ce qui concerne les commentaires précédents, vous voudriez déléguer à : 'Self.class' code> comme
: auto code> délégués à l'instance, pas la classe lorsqu'elle est évaluée.
Vous pouvez également faire cela:
def self.color your_args; your_expression end define_method :color, &method(:color)
Si c'est un simple rubis, alors en utilisant transférable a> est la bonne réponse Si ce serait des rails, j'aurais utilisé délégué , par exemple P> class Foo
delegate :colour, to: :class
def self.colour
"green"
end
end
irb(main):012:0> my_foo = Foo.new
=> #<Foo:0x007f9913110d60>
irb(main):013:0> my_foo.colour
=> "green"
Si vous déléguez au sein d'un ActiveSupport :: Préoccupation Code> Préoccupation, placez le
Délégué CODE> appel à l'extérieur du
inclus code> et
class_methods code > blocs
Sans vouloir débattre de la Colo (U) R du bikéché, il peut être judicieux d'utiliser l'anglais américain en code plutôt que le Commonwealth English.
C'est mon propre code personnel; Les seules personnes devraient toujours voir que c'est moi et quelques amis aussi de la Grande-Bretagne. Je vais utiliser un code américain en milieu de travail si le style house le confirme - je ne suis pas sur le point de l'utiliser dans la mienne.
@Chowlett, c'est certainement votre choix. Mais vous les mélangerez toujours, par ex. Lorsque vous accédez à votre
@Colour code> dans une méthode code> Initialiser code>.
En outre, je voudrais inverser et déléguer des méthodes de classe dans des méthodes d'interdiction, si vous n'avez pas besoin de l'état en premier lieu, vous devez utiliser une construction apatride.