Не так давно, занявшись изучением ruby, я столкнулся с тем, что не понимаю, что такое class методы, точнее в чем их отличие от instance и зачем они вообще нужны. В учебнике, который я изучаю на данный момент, эта тема была описана не достаточно подробно или я не дочитал до подробного описания, но в любом случае мне стало интересно разобраться и я полез искать ответы в google. Данный пост является всем тем, что мне удалось найти и понять. Конечно, для опытных ruby разработчиков тут интересного мало, но я надеюсь, что смогу помочь таким же новичкам в языке как и я. Если я вас заинтриговал — прошу под кат.
Для начала, давайте определимся: в ruby все объекты. Это нужно впитать, усвоить и всегда помнить. Даже классы — это объекты имя которых — константа. И с каждым объектом можно совершать какие либо действия благодаря методам. Следовательно, возникает резонный вопрос: можно ли что-то делать с классами благодаря методам?
Думаю, что многие догадаются, что ответ на этот вопрос будет положительный, даже больше скажу: в ruby каждый метод является либо методом экземпляра (instance method) либо методом класса (class method соответственно). Отличаются они, кроме способа создания, только тем, что первые методы вызываются с экземпляром класса, а вторые — с классом. Важно понимать, в чем заключается различие между этими двумя методами и уметь определять когда и какой метод использовать. Поэтому рассмотрим методы класса (class methood), но для начала напомню, а может для кого и расскажу, что такое методы экземпляра.
Методы экземпляра (instance metods):
Часто используемые, обычно с таких методов начинается изучение языка практически во всех учебниках. Обычно создаются они тремя способами:
# Способ 1
class Test
def foo
puts 'Метод экземпляра'
end
end
a = Test.new
a.foo # => "Метод экземпляра"
# Способ 2
class Test
attr_accessor :foo
end
a = Test.new
a.foo = 'Метод экземпляра'
puts a.foo
# Способ 3
class Test; end
a = Test.new
def a.foo
puts 'Метод экземпляра'
end
Test.new.foo # => "Метод экземпляра"
Ничего сложного и все понятно. Используются они везде и всюду. Например: в rails модули экземпляра могут отвечать за создание и удаление тех же постов, ну или часто встречающийся метод — метод сложения.
Методы класса (class methods):
Руби очень гибкий и лаконичный язык, поэтому он предоставляет нам аж целых четыре различных способа создания методов класса:
# Способ 1
class Test
def self.foo
puts 'Метод класса'
end
end
Test.foo # => "Метод класса"
# Способ 1
class Test
def Test.foo
puts 'Метод класса'
end
end
Test.foo # => "Метод класса"
# Способ 3
class Test
class << self
def foo
puts 'Метод класса'
end
end
end
Test.foo # => "Метод класса"
# Способ 4
class Test; end
def Test.foo
puts 'Метод класса'
end
Test.bar # => "Метод класса"
Собственно создание данных методов тоже не представляет сложностей. Все представленные способы одинаково работают и единственное что их различает — вкус разработчика. В первом и во втором способе используется одна и та же конструкция, с единственным различием в переменной self
. Третий случай интересен тем, что создается анонимный класс, а последний — очень похож на создание синглтона. Когда я вижу код, похожий на self.name_class_method
, я понимаю, что это метод класса, поэтому я соглашаюсь со мнением людей, утверждающих, что в первом случае код более читаемый, так как он создает меньше путаницы.
Когда стоит пользоваться данными методами? Обычно их используют, когда вы имеете дело не с отдельными экземплярами класса. Самый банальный пример — методы валидации в rails. Выглядит код примерно так:
module ActiveRecord
class Base
def self.validates_of(...)
# Код валидации
end
end
end
class Foo < ActiveRecord::Base
validates_of :bar # не посредственное использование метода в классе
end
К этому примеру можно так же отнести все методы arrt_*
семейства. Так же, есть замечательный метод new
, который содержат в себе все экземпляры класса Class
.
Взаимодействие instance и class методов между собой.
Бывают случаи, когда в методе класса необходимо вызвать метод экземпляра, ruby для этих целей предоставляет элегантный способ:
# Использование instance метода в class методе.
class Test
def instance_method
# код метода
end
def self.class_method
a = Test.new
a.instance_method
end
end
А иногда бывают случаи когда наоборот, нужно вызвать метод класса в методе экземпляра. Для этих целей нужно использовать метод class
:
# Использование class метода в instance методе.
class Test
def self.class_method
puts self
end
def instance_method
self.class.class_method
end
end
Важно понимать, что self
возвращает значение конкретного класса, поэтому иногда происходит путаница, когда нужно, что бы и наследники такого класса возвращали метод родителя. Для этих целей нужно использовать вместо self
непосредственно имя класса родителя:
class Test1
def self.class_method
"foo"
end
def make1
self.class.class_method
end
def make2
Test1.class_method
end
end
class Test2 < Test1
def self.default_make
"abc"
end
end
b = Test2.new
b.make1 # => "abc"
b.make2 # => "foo"
Автор: Fikys