litbaza книги онлайнРазная литератураCrystal Programming. Введение на основе проекта в создание эффективных, безопасных и читаемых веб-приложений и приложений CLI - Джордж Дитрих

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 17 18 19 20 21 22 23 24 25 ... 75
Перейти на страницу:
в качестве держателя переменных и методов. Примером этого является модуль Base64 из стандартной библиотеки – он просто предоставляет некоторые служебные методы и не предназначен для включения в класс:

# Prints "Crystal Rocks!":

p Base64.decode_string("Q3J5c3RhbCBSb2NrcyE=")

В данном случае Base64 – это просто группа связанных методов, доступ к которым осуществляется непосредственно из модуля. Это общий шаблон, который помогает организовать методы и классы.

Подробнее о различных вариантах использования модулей будет рассказано позже в этой книге. Мы многое узнали о классах и объектах, но не все объекты ведут себя одинаково. Далее давайте разберемся, в чем разница между значениями и ссылками.

Значения и ссылки – использование структур

По умолчанию объекты Crystal размещаются в памяти и управляются сборщиком мусора. Это означает, что вам не нужно беспокоиться о том, где находится каждый объект в памяти и как долго он должен жить - среда выполнения позаботится о том, чтобы учесть, на какие объекты все еще ссылаются некоторые переменные, и освободит все остальные, автоматически освобождая ресурсы. Переменные не будут хранить объект как таковой - они будут хранить ссылку, указывающую на объект. Все это работает прозрачно, и беспокоиться об этом не нужно.

Вышесказанное справедливо для всех объектов, созданных из классов; типы этих объектов являются ссылочными типами. Но есть и другой тип объектов: типы значений.

На следующей диаграмме вы можете увидеть цепочку наследования некоторых типов. Те, которые являются ссылками, наследуются от ссылочного класса, в то время как те, которые являются значениями, наследуются от структуры Value. Все они наследуются от специального базового типа Object:

Рисунок 3.1 - Иерархия типов, показывающая, как ссылки связаны со значениями.

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

Вы можете создавать свои собственные структуры. Они очень похожи на классы тем, что у них также есть переменные экземпляра и методы:

struct Address

    property state : String, city : String

    property line1 : String, line2 : String

    property zip : String

    def initialize(@state, @city, @line1, @line2, @zip)

    end

end

Структуры и классы – это все типы объектов, и их можно использовать для ввода любой переменной, включая объединения типов. Например, давайте сохраним адрес внутри класса Person:

class Person

    property address : Address?

end

В данном случае переменная экземпляра @address имеет тип Address? это сокращение от Address | Nil. Поскольку начального значения нет и эта переменная не назначается в методе initialize, она начинается с nil. Использование структуры является простым:

address = Address.new("CA", "Los Angeles", "Some fictitious line", "First house", "1234")

person1 = Person.new

person2 = Person.new

person1.address = address

address.zip = "ABCD"

person2.address = address

puts person1.address.try &.zip

puts person2.address.try &.zip

Мы начали этот пример с создания адреса и двух persons – в общей сложности трех объектов: одного объекта-значения и двух объектов-ссылок. Затем мы присвоили адрес из локальной переменной address переменной экземпляра @address для person1. Поскольку адрес является значением, эта операция копирует данные. Мы изменяем его и присваиваем @address person2. Обратите внимание, что изменение не влияет на person1 – значения всегда копируются. Наконец, мы показываем почтовый индекс в каждом адресе. Нам нужно использовать метод try для доступа к свойству zip только в том случае, если на данный момент значение union не равно nil, поскольку компилятор не может определить это самостоятельно.

Поэкспериментируйте с изменением адреса класса и повторным запуском предыдущего кода. На этот раз у обоих пользователей будет одинаковый почтовый индекс. Это происходит потому, что ссылки не копируются при присвоении, поэтому все переменные будут ссылаться на один и тот же объект address.

Значения структуры всегда копируются, когда вы присваиваете их из одной переменной в другую, когда вы передаете их в качестве аргументов при вызове метода или когда вы получаете их из возвращаемого значения при вызове метода. Это известно как семантика "по значению"; таким образом, рекомендуется , чтобы структуры были небольшими с точки зрения объема их памяти. Из этого правила есть интересное и полезное исключение: когда тело метода просто возвращает переменную экземпляра напрямую, копия удаляется, и к значению осуществляется прямой доступ. Давайте рассмотрим пример:

struct Location

    property latitude = 0.0, longitude = 0.0

end

class Building

    property gps = Location.new

end

building = Building.new

building.gps.latitude = 1.5

p store

В предыдущем примере мы создали структурный тип Location, который имеет два свойства, и класс Building, который имеет одно свойство. Макрос property gps сгенерирует метод с именем def gps; @gps; end для получателя - обратите внимание, что этот метод просто возвращает переменную экземпляра напрямую, что соответствует правилу исключения копирования. Если бы этот метод был каким-то другим, этот пример не сработал бы.

Строка building.gps.latitude = 1.5 вызывает метод gps и получает результат, затем вызывает параметр latitude=setter с значением 1.5 в качестве аргумента. Если бы возвращаемое значение gps было скопировано, то средство настройки работало бы с копией структуры и не влияло бы на значение, хранящееся в переменной building. Попробуйте поэкспериментировать с добавлением пользовательского определения для метода gps.

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

Общие (Generic) классы

Общий класс (или структура) создается на основе одного или нескольких неизвестных типов, которые определяются только позже, когда вы создаете экземпляр указанного класса. Это звучит сложно, но вы уже использовали некоторые общие классы раньше. Array является наиболее распространенным: заметили ли вы, что нам всегда нужно указывать тип данных, которые содержит массив? Недостаточно сказать, что данная переменная является массивом — мы должны сказать, что это массив строк или

1 ... 17 18 19 20 21 22 23 24 25 ... 75
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?