Методы, определенные в классе: методы экземпляра :
class Animal
def dog
"woof"
end
def cat
"meow"
end
end
Они названы так, потому что они отвечают на любой экземпляр класса, что означает, что их получатель должен быть экземпляром класса:
Animal.instance_methods(false)
#=> [:dog, :cat]
animal = Animal.new
#=> #<Animal:0x00005bfb0c55ae98>
animal.dog
#=> "woof"
Animal.dog
#=> NoMethodError (undefined method `dog' for Animal:Class)
Чтобы определить метод, получателем которого является класс ( метод класса ), мы определяем метод в классе синглтона класса. Для класса Animal
мы могли бы написать одно из следующих:
class Animal
class << Animal
def pig(n)
"#{n} little pigs"
end
end
end
Animal.methods(false)
#=> [:pig]
Animal.pig(3)
#=> "3 little pigs"
или
Animal.define_singleton_method(:pig) do |n|
"#{n} little pigs"
end
Animal.methods(false)
#=> [:pig]
Animal.pig(3)
#=> "3 little pigs"
Строка class < Animal
1 изменяет область действия на одноэлементный класс Animal
, в результате чего значение self
также изменяется на этот класс.
Так, как это связано с вопросом, то есть определением методов def self.my_method ...
? Краткий ответ: нет необходимости определять методы таким образом. Пожалуйста, будьте терпеливы - я доберусь до этого.
Обратите внимание, что метод pig
, определенный в одноэлементном классе Animal
, наследуется одноэлементным классом подклассов Animal
:
class Swine < Animal
end
Swine.instance_methods & [:dog, :cat]
#=> [:dog, :cat]
Swine.methods & [:pig]
#=> [:pig]
Мы также можем определить методы для многих уникальных объектов . Рассмотрим animal
, экземпляр Animal
:
animal.define_singleton_method(:rodent) do |n|
"I'm rodent ##{n}"
end
animal.rodent(3241)
#=> "I'm rodent #3241"
animal
- единственный получатель, на который ответит этот метод:
Animal.new.rodent(55)
#=> #NoMethodError (undefined method `rodent' for
# #<Animal:0x00005bfb0c530670>)
Фактически, мы можем определить методы для каждого объекта, который имеет одноэлементный класс, который является большинством объектов:
str = "cow"
str.define_singleton_method(:greeting) { "moo" }
str.greeting
#=> "moo"
arr = [1,2]
arr.define_singleton_method(:greeting) { "I'm an array" }
arr.greeting
#=> "I'm an array"
module M; end
M.define_singleton_method(:greeting) { "I'm a module" }
M.greeting
#=> "I'm a module"
piggy = Animal.method(:pig)
#=> #<Method: Animal.pig>
piggy.define_singleton_method(:greeting) {
"I'm a singleton method" }
piggy.greeting
#=> "I'm a singleton method"
Мы можем сделать это со всеми объектами Ruby, которые имеют одноэлементный класс. Это включает в себя все объекты, кроме тех, которые имеют непосредственные значения (объекты, переданные по значению), которые включают nil
, true
, false
, целые числа, символы и некоторые числа с плавающей точкой. Кроме того, объекты, которые были заморожены (например, "Hi".freeze
), не имеют одноэлементного класса.
Предположим, теперь мы пишем
class Animal
def Animal.pig(n)
"#{n} little pigs"
end
end
или (тоже самое)
class Animal
def self.pig(n)
"#{n} little pigs"
end
end
(Мы наконец там!)
Что это за новый способ определения метода? На самом деле это просто краткий способ определения метода в синглтон-классе Animal
. Думайте об этом как о синтаксическом сахаре . Так же, как запись 2 + 2
указывает Ruby выполнить 2.+(2)
, Animal.
или self.
в первой строке определения метода, просто указывает Ruby выполнить следующее.
class Animal
class << self
def pig(n)
"#{n} little pigs"
end
end
end
Другими словами, создание метода класса с помощью записи def Animal.my_method...
или def self.my_method...
вообще не требуется; это просто для удобства Ruby-кодеров.
1 Эта строка обычно пишется class << self
, что допустимо, так как self
равно Animal
при выполнении строки. Использование << self
- просто удобство, если класс будет переименован.