Давайте прямо сейчас перечитаем четвертую каплю, чтобы вспомнить о реализации ООП в Руби. Повторили? Идем дальше. В этой капле мы подрежем все образовавшиеся хвосты, связанные с объектно-ориентированным программированием на Руби.
Давайте взглянем еще раз на код из четвертой капли:
Похожая программа:
Когда определяется новый класс (обычно используя
Использование
В коде мы определили два метода (
Заметим, что в
В четвертой капле мы уже говорили о наследовании в «зоопарке», однако там мы использовали
В этом примере мы отказались еще и от кошек :) Как всегда самый простой класс выше по иерархии. Единственное отличие — это ключевое слово
Даже новосозданный, пустой объект уже «откликается» на ряд методов. Выведем список этих методов кодом:
Вы можете узнать, может ли объект ответить на сообщение, которое вы хотите отправить ему с помощью метода
Блок (
Еще немного полезной информации, чуть важной теории и много кода. Это была последняя подобная капля, обещаю ;) Дальше начнем кодить по-серьезному, а пока у вас есть время пробежаться глазами по всем каплям и собрать всю инфу в кучу. Комментарии ожидаемы!
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."
endProc в Руби
Блок (
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: Как видите, теперь все капли в одном блоге. Не забудьте подписаться на него — так следить за выпусками еще легче!
