Pull to refresh

Покорим Ruby вместе! Капля восьмая

Reading time 3 min
Views 28K
Давайте прямо сейчас перечитаем четвертую каплю, чтобы вспомнить о реализации ООП в Руби. Повторили? Идем дальше. В этой капле мы подрежем все образовавшиеся хвосты, связанные с объектно-ориентированным программированием на Руби.


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: Как видите, теперь все капли в одном блоге. Не забудьте подписаться на него — так следить за выпусками еще легче!

Tags:
Hubs:
+23
Comments 33
Comments Comments 33

Articles