Давайте прямо сейчас перечитаем четвертую каплю, чтобы вспомнить о реализации ООП в Руби. Повторили? Идем дальше. В этой капле мы подрежем все образовавшиеся хвосты, связанные с объектно-ориентированным программированием на Руби.
Давайте взглянем еще раз на код из четвертой капли:
Похожая программа:
Когда определяется новый класс (обычно используя
Использование
В коде мы определили два метода (
Заметим, что в
В четвертой капле мы уже говорили о наследовании в «зоопарке», однако там мы использовали
В этом примере мы отказались еще и от кошек :) Как всегда самый простой класс выше по иерархии. Единственное отличие — это ключевое слово
Даже новосозданный, пустой объект уже «откликается» на ряд методов. Выведем список этих методов кодом:
Вы можете узнать, может ли объект ответить на сообщение, которое вы хотите отправить ему с помощью метода
Блок (
Еще немного полезной информации, чуть важной теории и много кода. Это была последняя подобная капля, обещаю ;) Дальше начнем кодить по-серьезному, а пока у вас есть время пробежаться глазами по всем каплям и собрать всю инфу в кучу. Комментарии ожидаемы!
PS: Как видите, теперь все капли в одном блоге. Не забудьте подписаться на него — так следить за выпусками еще легче!
new и initialize
Давайте взглянем еще раз на код из четвертой капли:
class Dog
def set_name( aName )
@myname = aName
end
def get_name
return @myname
end
def gav
return 'r-r-r-r!'
end
end
dog1 = Dog.new
dog1.set_name( 'Fido' )
puts(dog1.get_name)
puts(dog1.gav)
Похожая программа:
class Dog
def initialize(name, age)
@name = name
@age = age
end
def get_name
return @name
end
def get_age
return @age
end
end
d = Dog.new('Fido', 2)
puts "My name is #{d.get_name} and I'm #{d.get_age}"
Когда определяется новый класс (обычно используя
class Name ... end
), создается объект типа Class
. Когда вызывается Name.new
для создания нового объекта, вызывается метод экземляра new
из Class
, который в свою очередь активизирует allocate
, чтобы выделить память для объекта перед тем, как будет вызван метод initialize
нового объекта. Фазы конструкции и инициализации объекта отдельны и могут быть переписаны. Инициализация происходит через метод экземпляра initialize
, конструкция — через new
. initialize
— не конструктор (спорно, конечно, но в разных источниках — разное мнение).Использование
initialize
имеет два явных преимущества над установкой переменных экземляра, используя методы типа set_name
. Прежде всего сложный класс может содержать множество переменных экземпляра и все их можно объявить с помощью одной строки с initialize
без необходимости писать методы для каждой. Также если все переменные объявляются во время создания объекта (как написано выше непосредственно после new
вызывается именно initialize
), у вас никогда не останется неопределенных переменных (nil
).Еще проще
В коде мы определили два метода (
get_name
и get_age
), чтобы возвращать две переменные экземпляра. Так как это простая и часто используемая идиома, Руби предоставляет упрощение: attr_reader
определит эти методы за нас:class Dog
attr_reader :name, :age
def initialize(name, age)
@name = name
@age = age
end
end
d = Dog.new("Fido", 2)
puts "My name is #{d.name} and I'm #{d.age}"
Заметим, что в
attr_reader
используются symbol'ы (см. прошлую каплю) и то, как изменился запрос значения в выводе. attr_writer
определит set
-методы (set_name
в первом листинге, например), а attr_accessor
комбинирует возможности ридера и райтера (см. пример с зоопарком в четвертой капле).initialize + наследование
В четвертой капле мы уже говорили о наследовании в «зоопарке», однако там мы использовали
attr_accessor
. Как же будет выглядеть наследование, если мы от них откажемся и вернемся к initialize
и методам? Не намного сложнее:class Pet
def initialize(name, age)
@name = name
@age = age
end
def get_name
return @name
end
def get_age
return @age
end
end
class Dog < Pet
def initialize(name, age)
@name = name
@age = age
super
end
end
class Snake < Pet
def initialize(name, age, length)
@name = name
@age = age
@length = length
super(name, age)
end
def get_length
return @length
end
end
d = Dog.new('Fido', 2)
s = Snake.new('Lili', 2, 85)
puts "Dog: My name is #{d.get_name} and I'm #{d.get_age}"
puts "Snake: My name is #{s.get_name}, I'm #{s.get_age} & I'm #{s.get_length} cm"
В этом примере мы отказались еще и от кошек :) Как всегда самый простой класс выше по иерархии. Единственное отличие — это ключевое слово
super
. Им обозначаются переменные, которые классы-потомки должны передать вышестоящему классу. Просто super
передаст все переменные, super()
— ни одной. Общие методы уходят в класс-родитель, у потомков остается только инициализация переменных и собственные методы.Методы объектов
Даже новосозданный, пустой объект уже «откликается» на ряд методов. Выведем список этих методов кодом:
puts d.methods.sort
, где d
— любой объект. Из всех стоит выделить object_id
(у всех объектов Руби есть уникальный номер, который и выведет метод), class
(выведет класс, которому принадлежит объект) и обратный instance_of?
(вернет true, если объект принадлежит классу из параметра, например, puts 10.instance_of?(Fixnum)
)Вы можете узнать, может ли объект ответить на сообщение, которое вы хотите отправить ему с помощью метода
respond_to?
. Чаще всего он используется в условии:if d.respond_to?("gav")
d.gav
else
puts "Sorry, don't understand."
end
Proc в Руби
Блок (
do ... end
или {...}
) объектом не является, но он может быть преобразован в объект класса Proc
с помощью метода lambda
. Активизирует блок метод call
из Proc
. Методы могут содержать в себе proc'и:def method proc
puts 'Start of method'
proc.call
puts 'End of method'
end
say = lambda {puts 'Hello'}
method say
Эпилог
Еще немного полезной информации, чуть важной теории и много кода. Это была последняя подобная капля, обещаю ;) Дальше начнем кодить по-серьезному, а пока у вас есть время пробежаться глазами по всем каплям и собрать всю инфу в кучу. Комментарии ожидаемы!
PS: Как видите, теперь все капли в одном блоге. Не забудьте подписаться на него — так следить за выпусками еще легче!