Pull to refresh

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

Programming
В этой капле мы еще раз углубимся ООП, выясняя новые методики работы с классами, объектами, модулями, которые обязательно пригодятся нам в серьезном программировании.

Множественные конструкторы


Что если мы хотим иметь несколько конструкторов для объекта? Ничто не мешает нам создать дополнительные методы класса, которые возвращают новые объекты. В следующем примере мы описываем котов, имеющих пять параметров: вес, высота, и три на окрас. Мы создадим дополнительные методы, которые определят некоторые типы котов “по умолчанию” (например, черный кот или толстый кот):

class SuperCat

def initialize(height, weight, tail_color, head_color, legs_color)
@height, @weight, @tail_color, @head_color, @legs_color = height, weight, tail_color, head_color, legs_color
end

def
SuperCat.white_cat(height, weight)
new(height, weight, "white", "white", "white")
end

def
SuperCat.black_cat(height, weight)
new(height, weight, "black", "black", "black")
end

def
SuperCat.big_cat(tail_color, head_color, legs_color)
new(100, 100, tail_color, head_color, legs_color)
end

end
a = SuperCat.new(10, 15, "white", "black", "white")
b = SuperCat.black_cat(13, 20)
c = SuperCat.big_cat("white", "red", "red")
p(a); p(b); p(c)


Имеет ли здесь место слово “конструктор”? Мы оставим этот вопрос для юристов :)

Развитие конструкторов


Объекты становятся все сложнее, они собирают все больше атрибутов, которые необходимо инициализировать при создании объекта. Отвечающий за это конструктов становится длинным и обременительным, заставляя нас пересчитывать параметры и переносить строки.

Один из способов справиться с этой сложностью – передать блок методу initialize. Затем мы можем использовать блок для инициализации объекта. Будем использовать метод instance_eval вместо eval:
class HyperCat
attr_accessor :name,
:height, :weight, :age,
:tail_color, :head_color, :legs_color

def initialize(&block)
instance_eval &block
end

# Другие методы...
end

pussy = HyperCat.new do
self
.name = "Pussy"
self.height = 10
self.weight = 12
self.age = 3.2
self.tail_color = "gray"
self.head_color = "gray"
self.legs_color = "white"
end

p pussy

Мы используем аксессоры в атрибутах, так легче передавать им значения. Также нам необходимо ссылаться на self, потому что метод назначения значения всегда берет явного приемника чтобы создать локальную переменную.

Контроль доступа к методам


В Руби объект в основном определяется предоставляемым им интерфейсом, методом, с помощью которого он становится доступным другим. Однако, при написании класса нам часто нужны вспомогательные методы, используемые внутри класса, но опасные, если они доступны извне. Вот где нам поможет метод private класса Module.

Мы можем использовать private двумя способами. Если вызвать private без параметров в теле класса, все методы ниже станут приватными. Или вы можете передать список методов в виде символов в качестве параметров private:
class Bank
def open_safe
# ...
end

def
close_safe
# ...
end

private :open_safe, :close_safe

def make_withdrawal(amount)
if access_allowed
open_safe
get_cash(amount)
close_safe
end
end

# остальное - приватное

private

def get_cash
# ...
end

def
access_allowed
# ...
end
end


Копирование объектов


Методы clone и dup создают копии вызывающего элемента. Метод dup копирует только содержание объекта, в то время как clone берет и такие вещи, как синглтон классы, связанные с объектом:
s1 = "cat"

def s1.upcase
"CaT"
end

s1_dup = s1.dup
s1_clone = s1.clone
s1 #=> "cat"
s1_dup.upcase #=> "CAT" (синглтон метод не скопировался)
s1_clone.upcase #=> "CaT"


Углубляясь в модули


Перелистнем на одну каплю назад и вспомним, что такое модули и как их применять, в частности, обратим внимание на примеси и пример, приведенный там.
Но что случится при смешивании с нашими методами модуля? Если вы думаете, что будут включены как методы класса, то Руби так не поступает. Для этого можно сделать так:
module MyMod

def meth
puts "Метод экземпляра модуля"
puts "может стать методом класса."
end

end

class
MyClass

class << self # Здесь self это MyClass
include MyMod
end

end

Здесь нам пригодится метод extend – с ним пример становится гораздо проще:
class MyClass
extend MyMod
end
MyClass.meth


Создание Struct’ов


Иногда нам нужно просто сгруппировать некоторые данные без дальнейшей обработки. Мы можем создать класс:
class ExtraCat

attr_accessor :name, :age, :weight

def initialize(name, age, weight)
@name, @age, @weight = name, age, weight
end

end

lucky = ExtraCat.new("Lucky", 2, 4)
Это, конечно, работает, но здесь одни повторения. Вот почему пригодился класс Struct. Как attr_accessor за нас определяет необходимые методы, так и Struct определяет классы, содержащие одни атрибуты. Эти классы называются структурными шаблонами (structure templates).
ExtraCat = Struct.new("ExtraCat", :name, :age, :weight)
lucky = ExtraCat.new("Lucky", 2, 4)

Эпилог


В общем, набрал я еще тем для изучения, так что рельсы опять идут (прокладываются?) лесом :) Держитесь, новички, но так надо — зато дальше будет легче, обещаю! Да, и, наконец, я соорудил подсветку кода, оказывается, все лишь надо было Windows Live Writer + Visual Studio + VSPaste + HTML Snippet + PowerGREP + регулярные выражения + некоторая работа ручками :( Но теперь могу вас радовать симпатичным кодом, правильной орфографией и больше не боюсь потерять статьи ;) И, конечно, все статьи цикла ждут вас в блоге Стартап «Программист»
Tags:rubyООПstructконструкторучебникtutorial
Hubs: Programming
Total votes 36: ↑26 and ↓10 +16
Views10K

Popular right now

Ruby Backend-разработчик
from 150,000 ₽KUPIBILET.RUСанкт-Петербург
QA Automation Engineer (Ruby)
from 130,000 to 160,000 ₽CV RecruitmentRemote job
Ruby on Rails - Middle
from 150,000 to 180,000 ₽igooodsСанкт-ПетербургRemote job
Ruby Developer - Middle (remote)
from 1,600 to 3,000 $datarocketsRemote job
Программист Ruby
from 120,000 to 300,000 ₽ReineМосква

Top of the last 24 hours