11.1.14. Классы, содержащие только данные (Struct)

Иногда нужно просто сгруппировать взаимосвязанные данные, не определяя никакие специфические методы обработки. Можно для этого создать класс:

class Address

 attr_accessor :street, :city, :state

 def initialize(street1, city, state)

  @street, @city, @state = street, city, state

 end

end

books = Address.new('411 Elm St', 'Dallas', 'TX')

Такое решение годится, но каждый раз прибегать к нему утомительно; к тому же здесь слишком много повторов. Тут-то и приходит на помощь встроенный класс Struct. Если вспомогательные методы типа attr_accessor определяют методы доступа к атрибутам, то Struct определяет целый класс, который может содержать только атрибуты. Такие классы называются структурными шаблонами.

Address = Struct.new('Address', :street, :city, :state)

books = Address.new('411 Elm St', 'Dallas', 'TX')

Зачем передавать первым параметром конструктора имя создаваемой структуры и присваивать результат константе (в данном случае Address)?

При вызове Struct.new для создания нового структурного шаблона на самом деле создается новый класс внутри самого класса Struct. Этому классу присваивается имя, переданное первым параметром, а остальные параметры становятся именами его атрибутов. При желании к вновь созданному классу можно было бы получить доступ, указав пространство имен Struct:

Struct.new('Address', :street, :city, :state)

books = Struct::Address.new('411 Elm St', 'Dallas', 'TX')

Создав структурный шаблон, вы вызываете его метод new для создания новых экземпляров данной конкретной структуры. Необязательно присваивать значения всем атрибутам в конструкторе. Опущенные атрибуты получат значение nil. После того как структура создана, к ее атрибутам можно обращаться с помощью обычного синтаксиса или указывая их имена в скобках в качестве индекса, как будто структура - это объект класса Hash. Более подробную информацию о классе Struct можно найти в любом справочном руководстве (например, на сайте ruby.doc.org).

Кстати, не рекомендуем создавать структуру с именем Tms, так как уже есть предопределенный класс Struct::Tms.

11.1.15. Замораживание объектов

Иногда необходимо воспрепятствовать изменению объекта. Это позволяет сделать метод freeze (определенный в классе Object). По существу, он превращает объект в константу.

Попытка модифицировать замороженный объект приводит к исключению TypeError. В листинге 11.8 приведено два примера.

Листинг 11.8. Замораживание объекта

str = 'Это тест. '

str.freeze

begin

 str << ' He волнуйтесь.' # Попытка модифицировать.

rescue => err

 puts '#{err.class} #{err}'

end

arr = [1, 2, 3]

arr.freeze

begin

 arr << 4 # Попытка модифицировать.

rescue => err

 puts '#{err.class} #{err}'

end

# Выводится:

# TypeError: can't modify frozen string

# TypeError: can't modify frozen array

Однако имейте в виду, что метод freeze применяется к ссылке на объект, а не к переменной! Это означает, что любая операция, приводящая к созданию нового объекта, завершится успешно. Иногда это противоречит интуиции. В примере ниже мы ожидаем, что операция += не выполнится, но все работает нормально. Дело в том, что присваивание — не вызов метода. Эта операция воздействует на переменные, а не на объекты, поэтому новый объект создается беспрепятственно. Старый объект по-прежнему заморожен, но переменная ссылается уже не на него.

str = 'counter-'

str.freeze

str += 'intuitive' # 'counter-intuitive'

arr = [8, 6, 7]

arr.freeze

arr += [5, 3, 0, 9] # [8, 6, 7, 5, 3, 0, 9]

Почему так происходит? Предложение a += x семантически эквивалентно a = a + x. При вычислении выражения a + x создается новый объект, который затем присваивается переменной a! Все составные операторы присваивания работают подобным образом, равно как и другие методы. Всегда задавайте себе вопрос: «Что я делаю — создаю новый объект или модифицирую существующий?» И тогда поведение freeze не станет для вас сюрпризом.

Существует метод frozen?, который сообщает, заморожен ли данный объект.

hash = { 1 => 1, 2 => 4, 3 => 9 }

hash.freeze

arr = hash.to_a

puts hash.frozen?  # true

puts arr.frozen?   # false

hash2 = hash

puts hash2.frozen? # true

Как видите (на примере hash2), замораживается именно объект, а не

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату