Выбрать главу

first = Person.new("Adam") # This will have @id = 1

second = Person.new("Jess") # And this will have @id = 2

# @@next_id inside Person is now 3.

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

Подобно переменным класса, методы класса можно определить в самом классе, добавив к его имени префикс self. Посмотри:

class Person

    def self.reset_next_id

        @@next_id = 1

    end

end

Теперь вы можете вызвать Person.reset_next_id для выполнения этого действия, работая напрямую с классом. Отсюда становится ясно, что классы действительно являются объектами, поскольку у них есть данные и методы. Все это работает, как и ожидалось, и с наследованием подклассов.

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

Подобно переменным экземпляра, существуют вспомогательные макросы, помогающие предоставлять переменные класса с помощью методов класса, то есть class_getter, class_setter и class_property:

class Person

    class_property next_id

end

Теперь можно сделать Person.next_id = 3 или x = Person.next_id.

Работа с модулями

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

Давайте рассмотрим пример модуля, который определяет метод Say_name на основе некоторого существующего метода имени:

module WithSayName

    abstract def name : String

    def say_name

        puts "My name is #{name}"

    end

end

Это можно использовать с вашим классом Person:

class Person

    include WithSayName

    property name : String

    def initialize(@name : String)

    end

end

Здесь метод имени, ожидаемый WithSayName, создается макросом свойства. Теперь мы можем создать новый экземпляр Person и вызвать для него Say_name.

Модули можно использовать для ограничений типа и типа переменных. Когда это будет сделано, он укажет любой класс, включающий этот модуль. Учитывая ранее определенный код, мы можем сделать следующее:

def show(thing : WithSayName)

    thing.say_name

end

show Person.new("Jim")

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

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

Стандартная библиотека включает в себя несколько полезных модулей для указания характеристик некоторых классов:

Comparable: реализует все операторы сравнения при условии, что вы правильно реализовали оператор <=>. Классы, представляющие значения в естественном порядке, которые можно сортировать внутри контейнера, обычно включают этот модуль.

Enumerable: используется для коллекций, элементы которых можно перечислять один за другим. Класс должен реализовать each метод. передавая каждый элемент в блок. Этот модуль, в свою очередь, реализует несколько вспомогательных методов для управления коллекцией.

Iterable: это означает, что можно лениво перебирать включающую коллекцию. Класс должен реализовать each метод без получения блока и вернуть экземпляр Iterator. Модуль добавит множество полезных методов для преобразования этого итератора.