2010-03-27 21 views
282

En Ruby, ¿cómo se llama a un método de clase desde una de las instancias de esa clase? Digamos que tengoRuby: método de clase de llamada de la instancia

class Truck 
    def self.default_make 
    # Class method. 
    "mac" 
    end 

    def initialize 
    # Instance method. 
    Truck.default_make # gets the default via the class's method. 
    # But: I wish to avoid mentioning Truck. Seems I'm repeating myself. 
    end 
end 

la línea Truck.default_make recupera el valor predeterminado. Pero, ¿hay alguna forma de decir esto sin mencionar Truck? Parece que debería haberlo.

Respuesta

459

En lugar de referirse al nombre literal de la clase, dentro de un método de instancia que sólo puede llamar self.class.whatever.

class Foo 
    def self.some_class_method 
     puts self 
    end 

    def some_instance_method 
     self.class.some_class_method 
    end 
end 

print "Class method: " 
Foo.some_class_method 

print "Instance method: " 
Foo.new.some_instance_method 

Salidas:

 
Class method: Foo 
Instance method: Foo 
+5

Me gustaría ver algún atajo en ruby ​​para llamar a un método de clase desde una instancia. es decir:> some_class_method en lugar de self.class.some_class_method – phoet

+7

si bien esta es la respuesta correcta, es una pena que "self.class" escriba más y sea menos fácil de leer que el nombre de clase "Truck". oh bien ... –

+14

@MattConnolly, es relativo, si su nombre de clase es 'SalesforceSyncJob', entonces es más corto;) – drewish

5

Lo estás haciendo de la manera correcta. Los métodos de clase (similares a los métodos "estáticos" en C++ o Java) no son parte de la instancia, por lo que deben referenciarse directamente.

En ese sentido, en su ejemplo que sería mejor servido haciendo 'default_make' un método regular:

#!/usr/bin/ruby 

class Truck 
    def default_make 
     # Class method. 
     "mac" 
    end 

    def initialize 
     # Instance method. 
     puts default_make # gets the default via the class's method. 
    end 
end 

myTruck = Truck.new() 

Los métodos de clase son más útiles para las funciones de tipo utilitario que utilizan la clase. Por ejemplo:

#!/usr/bin/ruby 

class Truck 
    attr_accessor :make 

    def default_make 
     # Class method. 
     "mac" 
    end 

    def self.buildTrucks(make, count) 
     truckArray = [] 

     (1..count).each do 
      truckArray << Truck.new(make) 
     end 

     return truckArray 
    end 

    def initialize(make = nil) 
     if(make == nil) 
      @make = default_make() 
     else 
      @make = make 
     end 
    end 
end 

myTrucks = Truck.buildTrucks("Yotota", 4) 

myTrucks.each do |truck| 
    puts truck.make 
end 
+1

No estoy de acuerdo en que 'default_make' sea un método de instancia. Incluso si es más simple para estos ejemplos, no es la semántica correcta: el valor predeterminado es un producto de la clase, no los objetos que pertenecen a la clase. – Peter

+1

@Peter ¿te gustaría explicar eso en términos más simples? Estoy aprendiendo que las respuestas de Ruby y Maha me parecen perfectas. –

+1

@ MarlenT.B. mirando hacia atrás, no estoy seguro de que haya mucho que aprender aquí. Solo estaba discutiendo sobre dónde estaba el mejor lugar para poner el método, y ya no me creo mi propio argumento. :) – Peter

6

para acceder a un método de clase dentro de un método de instancia, haga lo siguiente:

self.class.default_make 

Aquí es una solución alternativa para su problema:

class Truck 

    attr_accessor :make, :year 

    def self.default_make 
    "Toyota" 
    end 

    def make 
    @make || self.class.default_make 
    end 

    def initialize(make=nil, year=nil) 
    self.year, self.make = year, make 
    end 
end 

Ahora vamos a utilizar nuestra clase:

t = Truck.new("Honda", 2000) 
t.make 
# => "Honda" 
t.year 
# => "2000" 

t = Truck.new 
t.make 
# => "Toyota" 
t.year 
# => nil 
+0

make no debe ser un método de instancia. es más un tipo de fábrica, que debería estar vinculada a la clase en lugar de a una instancia – phoet

+4

@phoet La palabra hacer marca la marca de un automóvil (como en Toyota, BMW, etc.) http://www.englishforums.com/Español /AMakeOfCar/crcjb/post.htm. La nomenclatura se basa en los requisitos del usuario –

156

Usando self.class.blah NO es lo mismo que usar ClassName.blah cuando se trata de la herencia.

class Truck 
    def self.default_make 
    "mac" 
    end 

    def make1 
    self.class.default_make 
    end 

    def make2 
    Truck.default_make 
    end 
end 


class BigTruck < Truck 
    def self.default_make 
    "bigmac" 
    end 
end 

ruby-1.9.3-p0 :021 > b=BigTruck.new 
=> #<BigTruck:0x0000000307f348> 
ruby-1.9.3-p0 :022 > b.make1 
=> "bigmac" 
ruby-1.9.3-p0 :023 > b.make2 
=> "mac" 
+52

Esto parece ser una respuesta a la respuesta aceptada en lugar de una respuesta a la pregunta. – zhon

+12

@zohn - cierto, pero este sigue siendo un contexto útil al considerar qué usar. –

+0

@MattSanders solo usa un comentario en esos casos. – nandinga

5

Si usted tiene acceso al método delegado se puede hacer esto:

[20] pry(main)> class Foo 
[20] pry(main)* def self.bar 
[20] pry(main)*  "foo bar" 
[20] pry(main)* end 
[20] pry(main)* delegate :bar, to: 'self.class' 
[20] pry(main)* end 
=> [:bar] 
[21] pry(main)> Foo.new.bar 
=> "foo bar" 
[22] pry(main)> Foo.bar 
=> "foo bar" 

alternativa, y probablemente más limpia si usted tiene más de un método o dos desea delegar a la clase & ejemplo:

[1] pry(main)> class Foo 
[1] pry(main)* module AvailableToClassAndInstance 
[1] pry(main)*  def bar 
[1] pry(main)*  "foo bar" 
[1] pry(main)*  end 
[1] pry(main)* end 
[1] pry(main)* include AvailableToClassAndInstance 
[1] pry(main)* extend AvailableToClassAndInstance 
[1] pry(main)* end 
=> Foo 
[2] pry(main)> Foo.new.bar 
=> "foo bar" 
[3] pry(main)> Foo.bar 
=> "foo bar" 

Una palabra de precaución:

no hacer simplemente al azar delegate todo lo que no cambia de estado a clase e instancia porque empezará a encontrarse con problemas de enfrentamiento de nombres extraños. Haga esto con moderación y solo después de que haya revisado, nada queda aplastado.

1

Aquí hay un enfoque sobre cómo puede implementar un método _class que funciona como self.class para esta situación.Nota: No utilice esto en el código de producción, esto es para el bien de interés-:)

Desde: Can you eval code in the context of a caller in Ruby? y también http://rubychallenger.blogspot.com.au/2011/07/caller-binding.html

# Rabid monkey-patch for Object 
require 'continuation' if RUBY_VERSION >= '1.9.0' 
class Object 
    def __; eval 'self.class', caller_binding; end 
    alias :_class :__ 
    def caller_binding 
    cc = nil; count = 0 
    set_trace_func lambda { |event, file, lineno, id, binding, klass| 
     if count == 2 
     set_trace_func nil 
     cc.call binding 
     elsif event == "return" 
     count += 1 
     end 
    } 
    return callcc { |cont| cc = cont } 
    end 
end 

# Now we have awesome 
def Tiger 
    def roar 
    # self.class.roar 
    __.roar 
    # or, even 
    _class.roar 
    end 
    def self.roar 
    # TODO: tigerness 
    end 
end 

Tal vez la respuesta correcta es enviar un parche para Ruby :)

2

Uno más:

class Truck 
    def self.default_make 
    "mac" 
    end 

    attr_reader :make 

    private define_method :default_make, &method(:default_make) 

    def initialize(make = default_make) 
    @make = make 
    end 
end 

puts Truck.new.make # => mac 
+0

Ruby versión 2.1 – Alexey

-6

similares a su pregunta, se puede utilizar:

class Truck 
    def default_make 
    # Do something 
    end 

    def initialize 
    super 
    self.default_make 
    end 
end 
Cuestiones relacionadas