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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 14 15 16 17 18 19 20 21 22 ... 75
Перейти на страницу:
class="p">Манипулирование данными с использованием переменных и методов экземпляра

Все данные внутри объекта хранятся в переменных экземпляра; их имена всегда начинаются с символа @. Существует несколько способов определить переменную экземпляра для класса, но одно правило является фундаментальным: их тип должен быть известен. Тип может быть либо указан явно, либо синтаксически выведен компилятором.

Начальное значение переменной экземпляра может быть задано либо внутри метода initialize, либо непосредственно в теле класса. В последнем случае он ведет себя так, как если бы переменная была инициализирована в начале метода initialize. Если переменная экземпляра не назначена ни в одном методе initialize, ей неявно присваивается значение nil.

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

class Point

    def initialize(@x : Int32, @y : Int32)

    end

end

origin = Point.new(0, 0)

В этом первом случае класс Point указывает, что его объекты имеют две целочисленные переменные экземпляра. Метод initialize будет использовать свои аргументы, чтобы предоставить им начальное значение:

class Cat

    @birthday = Time.local

    

    def adopt(name : String)

        @name = name

    end

end

my_cat = Cat.new

my_cat.adopt("Tom")

Теперь у нас есть класс, описывающий кошку. У него нет метода initialize, поэтому он ведет себя так, как если бы он был пустым. Переменная @birthday назначается Time.local. Это происходит внутри этого пустого метода initialize при создании нового экземпляра объекта. Предполагается, что тип является экземпляром Time, поскольку Time.local вводится так, чтобы всегда возвращать его. Переменная @name получает строковое значение из типизированного аргумента, но нигде не имеет начального значения, поэтому ее тип — String? (это также можно представить как String | Nil).

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

class Person

  def initialize(first_name, last_name)

    @name = first_name + " " + last_name

  end

end

person = Person.new("John", "Doe")

В этом примере переменная @name создается путем объединения двух аргументов с пробелами между ними. Здесь тип этой переменной невозможно определить без более глубокого анализа типов двух параметров и результата вызова метода +. Даже если бы аргументы были явно типизированы как String, информации все равно было бы недостаточно, поскольку метод + для строк может быть переопределен где-то в коде, чтобы возвращать какой-либо другой произвольный тип. В подобных случаях необходимо объявить тип переменной экземпляра:

class Person

  @name : String

  def initialize(first_name, last_name)

    @name = first_name + " " + last_name

  end

end

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

class Person

  def initialize(first_name, last_name)

    @name = "#{first_name} #{last_name}"

  end

end

В любой ситуации допускается явное объявление типа переменной экземпляра, возможно, для ясности.

Примечание

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

Переменные экземпляра представляют частное состояние объекта, и ими следует манипулировать только с помощью методов внутри класса. Их можно раскрыть через геттеры и сеттеры. Доступ к переменным экземпляра можно получить извне с помощью синтаксиса obj.@ivar, но это не рекомендуется.

Создание геттеров и сеттеров

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

class Person

    def initialize(@name : String)

    end

end

Мы уже можем создать нового человека и проверить его:

person = Person.new("Tony")

p person

Но было бы неплохо иметь возможность написать что-то вроде следующего, как если бы @name был доступен:

puts "My name is #{person.name}"

person.name — это просто вызов метода name объекта person. Помните, что круглые скобки необязательны для вызовов методов. Мы можем пойти дальше и создать именно этот метод:

class Person

    def name

        @name

    end

end

Теперь вызов person.name действителен, как если бы переменная экземпляра была доступна извне. В качестве дополнительного преимущества будущий рефакторинг может изменить внутреннюю структуру объекта и переопределить этот метод, не затрагивая пользователей. Это настолько распространено, что специально для этого существует служебный макрос:

class Person

    getter name

end

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

class Person

    getter name : String

    getter age = 0

    getter height : Float64 = 1.65

end

Несколько геттеров могут быть созданы в одной строке:

class Person

    getter name : String, age = 0, height : Float64 = 1.65

end

Для сеттеров логика очень похожа. Имена методов Crystal могут заканчиваться символом = для обозначения установщика. Если у него один параметр, его можно вызвать с помощью удобного синтаксиса:

class Person

    def name=(new_name)

        puts "The new name is #{new_name}"

    end

end

Этот метод name= можно вызвать следующим образом:

person = Person.new("Tony")

person.name = "Alfred"

Последняя строка представляет собой просто вызов метода и не меняет значение переменной экземпляра @name. Это то же самое, что написать person.name=("Alfred"), как если бы = была любая другая буква. Мы можем воспользоваться этим, чтобы написать метод

1 ... 14 15 16 17 18 19 20 21 22 ... 75
Перейти на страницу:

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