Pull to refresh

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

Reading time 6 min
Views 11K
В этой капле мы еще раз углубимся ООП, выясняя новые методики работы с классами, объектами, модулями, которые обязательно пригодятся нам в серьезном программировании.

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


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

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:
Hubs:
+16
Comments 21
Comments Comments 21

Articles