В этой капле мы еще раз углубимся ООП, выясняя новые методики работы с классами, объектами, модулями, которые обязательно пригодятся нам в серьезном программировании.
Что если мы хотим иметь несколько конструкторов для объекта? Ничто не мешает нам создать дополнительные методы класса, которые возвращают новые объекты. В следующем примере мы описываем котов, имеющих пять параметров: вес, высота, и три на окрас. Мы создадим дополнительные методы, которые определят некоторые типы котов “по умолчанию” (например, черный кот или толстый кот):
Имеет ли здесь место слово “конструктор”? Мы оставим этот вопрос для юристов :)
Объекты становятся все сложнее, они собирают все больше атрибутов, которые необходимо инициализировать при создании объекта. Отвечающий за это конструктов становится длинным и обременительным, заставляя нас пересчитывать параметры и переносить строки.
Один из способов справиться с этой сложностью – передать блок методу
Мы используем аксессоры в атрибутах, так легче передавать им значения. Также нам необходимо ссылаться на
В Руби объект в основном определяется предоставляемым им интерфейсом, методом, с помощью которого он становится доступным другим. Однако, при написании класса нам часто нужны вспомогательные методы, используемые внутри класса, но опасные, если они доступны извне. Вот где нам поможет метод
Мы можем использовать
Методы
Перелистнем на одну каплю назад и вспомним, что такое модули и как их применять, в частности, обратим внимание на примеси и пример, приведенный там.
Но что случится при смешивании с нашими методами модуля? Если вы думаете, что будут включены как методы класса, то Руби так не поступает. Для этого можно сделать так:
Здесь нам пригодится метод
Иногда нам нужно просто сгруппировать некоторые данные без дальнейшей обработки. Мы можем создать класс:
В общем, набрал я еще тем для изучения, так что рельсы опять идут (прокладываются?) лесом :) Держитесь, новички, но так надо — зато дальше будет легче, обещаю! Да, и, наконец, я соорудил подсветку кода, оказывается, все лишь надо было Windows Live Writer + Visual Studio + VSPaste + HTML Snippet + PowerGREP + регулярные выражения + некоторая работа ручками :( Но теперь могу вас радовать симпатичным кодом, правильной орфографией и больше не боюсь потерять статьи ;) И, конечно, все статьи цикла ждут вас в блоге Стартап «Программист»
Множественные конструкторы
Что если мы хотим иметь несколько конструкторов для объекта? Ничто не мешает нам создать дополнительные методы класса, которые возвращают новые объекты. В следующем примере мы описываем котов, имеющих пять параметров: вес, высота, и три на окрас. Мы создадим дополнительные методы, которые определят некоторые типы котов “по умолчанию” (например, черный кот или толстый кот):
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 + регулярные выражения + некоторая работа ручками :( Но теперь могу вас радовать симпатичным кодом, правильной орфографией и больше не боюсь потерять статьи ;) И, конечно, все статьи цикла ждут вас в блоге Стартап «Программист»