Методы класса Ruby против методов в собственных классах
Являются ли методы и методы класса в собственном классе (или метаклассе) этого класса всего лишь двумя способами определить одну вещь?
Иначе каковы различия?
class X
# class method
def self.a
"a"
end
# eigenclass method
class << self
def b
"b"
end
end
end
Делать X.a
а также X.b
вести себя по-другому в любом случае?
Я понимаю, что могу переписать или псевдоним методов класса, открыв собственный класс:
irb(main):031:0> class X; def self.a; "a"; end; end
=> nil
irb(main):032:0> class X; class << self; alias_method :b, :a; end; end
=> #<Class:X>
irb(main):033:0> X.a
=> "a"
irb(main):034:0> X.b
=> "a"
irb(main):035:0> class X; class << self; def a; "c"; end; end; end
=> nil
irb(main):036:0> X.a
=> "c"
4 ответа
Два метода эквивалентны. Версия 'eigenclass' полезна для использования методов attr_*, например:
class Foo
@instances = []
class << self;
attr_reader :instances
end
def initialize
self.class.instances << self
end
end
2.times{ Foo.new }
p Foo.instances
#=> [#<Foo:0x2a3f020>, #<Foo:0x2a1a5c0>]
Вы также можете использовать define_singleton_method
создать методы в классе:
Foo.define_singleton_method :bim do "bam!" end
В Ruby действительно нет таких вещей, как методы класса. Поскольку все является объектом в Ruby (включая классы), когда вы говорите def self.class_method
вы просто действительно определяете одноэлементный метод в экземпляре класса Class
, Итак, чтобы ответить на ваш вопрос, сказав
class X
def self.a
puts "Hi"
end
class << self
def b
puts "there"
end
end
end
X.a # => Hi
X.b # => there
это два способа сказать одно и то же. Оба эти метода являются просто одиночными (eigen, meta, ghost или как вы хотите их вызывать) методами, определенными в экземпляре вашего объекта Class, который в вашем примере был X
, Эта тема является частью метапрограммирования, и это забавная тема, которую вы должны проверить, если вы уже некоторое время используете Ruby. У прагматичных программистов есть отличная книга по метапрограммированию, на которую обязательно стоит взглянуть, если вы заинтересованы в этой теме.
Еще один некромант здесь, чтобы раскопать этот старый вопрос... Одна вещь, о которой вы можете не знать, это то, что пометка метода класса как private
(используя личное ключевое слово вместо :private_class_method
) отличается от маркировки метода собственного класса как такового.:
class Foo
class << self
def baz
puts "Eigenclass public method."
end
private
def qux
puts "Private method on eigenclass."
end
end
private
def self.bar
puts "Private class method."
end
end
Foo.bar
#=> Private class method.
Foo.baz
#=> Eigenclass public method.
Foo.qux
#=> NoMethodError: private method `qux' called for Foo:Class
# from (irb)
Следующий пример будет работать так, как задумал предыдущий:
class Foo
class << self
def baz
puts "Eigen class public method."
end
private
def qux
puts "Private method on eigenclass."
end
end
def bar
puts "Private class method."
end
private_class_method :bar
end
Foo.bar
#=> NoMethodError: private method `bar' called for Foo:Class
# from (irb)
Foo.baz
#=> Eigen class public method.
Foo.qux
#=> NoMethodError: private method `qux' called for Foo:Class
# from (irb)
Большинство методов экземпляров, используемых в Ruby, являются глобальными методами. Это означает, что они доступны во всех экземплярах класса, в котором они были определены. Напротив, одноэлементный метод реализован на одном объекте.
Существует очевидное противоречие. Ruby хранит методы в классах, и все методы должны быть связаны с классом. Объект, для которого определен одноэлементный метод, не является классом (это экземпляр класса). Если только классы могут хранить методы, как объект может хранить одноэлементный метод? При создании одноэлементного метода Ruby автоматически создает анонимный класс для хранения этого метода. Эти анонимные классы называются метаклассами, также известными как одноэлементные классы или собственные классы. Одноэлементный метод связан с метаклассом, который, в свою очередь, связан с объектом, для которого был определен одноэлементный метод.
Если в одном объекте определены несколько одноэлементных методов, все они хранятся в одном и том же метаклассе.
class Zen
end
z1 = Zen.new
z2 = Zen.new
def z1.say_hello # Notice that the method name is prefixed with the object name
puts "Hello!"
end
z1.say_hello # Output: Hello!
z2.say_hello # Output: NoMethodError: undefined method `say_hello'…
В приведенном выше примере метод say_hello был определен в экземпляре z1 класса Zen, но не в экземпляре z2.
В следующем примере показан другой способ определения одноэлементного метода с тем же результатом.
class Zen
end
z1 = Zen.new
z2 = Zen.new
class << z1
def say_hello
puts "Hello!"
end
end
z1.say_hello # Output: Hello!
z2.say_hello # Output: NoMethodError: undefined method `say_hello'…
В вышеприведенном примере класс << z1 изменяет текущее "я", чтобы оно указывало на метакласс объекта z1; затем он определяет метод say_hello в метаклассе.
Оба приведенных выше примера служат для иллюстрации работы одноэлементных методов. Однако существует более простой способ определения одноэлементного метода: использование встроенного метода define_singleton_method.
class Zen
end
z1 = Zen.new
z2 = Zen.new
z1.define_singleton_method(:say_hello) { puts "Hello!" }
z1.say_hello # Output: Hello!
z2.say_hello # Output: NoMethodError: undefined method `say_hello'…
Ранее мы узнали, что классы также являются объектами (экземплярами встроенного класса, называемого Class). Мы также узнали о методах класса. Методы класса - это не более чем одноэлементные методы, связанные с объектом класса.
Еще один пример:
class Zabuton
class << self
def stuff
puts "Stuffing zabuton…"
end
end
end
Все объекты могут иметь метаклассы. Это означает, что у классов также могут быть метаклассы. В приведенном выше примере class << self изменяет self, поэтому оно указывает на метакласс класса Zabuton. Когда метод определен без явного получателя (класс / объект, для которого будет определен метод), он неявно определяется в текущей области, то есть в текущем значении себя. Следовательно, метод stuff определен в метаклассе класса Zabuton. Приведенный выше пример является еще одним способом определения метода класса.
Читайте больше в этом посте о Ruby Classes.