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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 13 14 15 16 17 18 19 20 21 ... 75
Перейти на страницу:
его экземплярами. Например, все экземпляры класса String имеют метод size, который возвращает количество символов строки в виде объекта типа Int32. Аналогично, объекты типа Int32 имеют метод с именем +, который принимает другое число в качестве единственного аргумента и возвращает его сумму, как показано в следующем примере:

p "Crystal".size + 4 # => 11

Это то же самое, что и более явная форма:

p("Crystal".size().+(4)) # => 11

Это показывает, что все распространенные операторы и свойства — это просто вызовы методов.

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

file = File.new("some_file.txt")

puts file.gets_to_end

file.close

Здесь file — это объект типа File, показывающий, как можно открыть файл, прочитать все его содержимое, а затем закрыть его. Новый метод вызывается в File для создания нового экземпляра класса. Этот метод получает строку в качестве аргумента и возвращает новый объект File, открывая указанный файл. Отсюда внутренняя реализация этого файла в памяти скрыта и взаимодействовать с ним можно только вызовом других методов. get_to_end затем используется для получения содержимого файла в виде строки, а метод close используется для закрытия файла и освобождения некоторых ресурсов.

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

File.openCsome_file.txt") do |file|

    puts file.gets_to_end

end

В предыдущем фрагменте методу open передается блок, который получает в качестве аргумента файл (тот же, который возвращает new). Блок выполняется, а затем файл закрывается.

Возможно, вы заметили, что так же, как этот код вызывает метод gets_to_end объекта file, он также вызывает метод open класса File. Ранее вы узнали, что методы — это то, как мы общаемся с объектами, так почему же они используются и для взаимодействия с классом? Это очень важная деталь, о которой следует помнить: в Crystal все является объектами, даже классы. Все классы являются объектами типа Class, и их можно присваивать переменным точно так же, как простые значения:

p 23.class    # => Int32

p Int32.class # => Class

num = 10

type = Int32

p num.class == type # => true

p File.new("some_file.txt") # => #<File:some_file.txt>

file_class = File

p file_class.newCsome_file.txt") # => #<File:some_file.txt>

Теперь вы знаете, что примитивные значения — это объекты, экземпляры более сложных типов из классов стандартной библиотеки — это объекты, и что сами классы тоже являются объектами. Каждый объект имеет внутреннее состояние и раскрывает методы мышления поведения. Переменные используются для хранения этих объектов.

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

Создание собственных классов

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

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

class Person

end

person1 = Person.new

person2 = Person.new

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

p person1   # You can display any object and inspect it

p person1.to_s # Any object can be transformed into a String

p person1 == person2  # false. By default, compares by reference.

p person1.same?(person2) # Also false, same as above.

p person1.nil?  # false, person1 isn't nil.

p person1.is_a?(Person) # true, person1 is an instance of Person.

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

class Person

   def initialize(name : String)

      @name = name

      @age = 0

   end

   def age_up

      @age += 1

   end

   def name

      @name

   end

   def name=(new_name)

      @name = new_name

   end

end

Здесь мы создали более реалистичный класс Person с внутренним состоянием, состоящим из @name, String, @age и Int32. В классе есть несколько методов, которые взаимодействуют с этими данными, включая метод initialize, который создаст нового человека — ребенка.

Теперь давайте воспользуемся этим классом:

jane = Person.new("Jane Doe")

p jane # => #<Person:0x7f97ae6f3ea0 @name="Jane Doe", # @age=0>

jane.name = "Mary"

5.times { jane.age_up }

p jane # => #<Person:0x7f97ae6f3ea0 @name="Mary", @age=5>

В этом примере создается экземпляр Person путем передачи строки новому методу. Эта строка используется для инициализации объекта и в конечном итоге присваивается переменной экземпляра @name. По умолчанию объекты можно проверять с помощью метода верхнего уровня p, который показывает имя класса, адрес в памяти и значение переменных экземпляра. Следующая строка вызывает метод name=(new_name) — он может делать что угодно, но для удобства он обновляет переменную @name новым значением. Затем мы вызываем age_up пять раз и снова проверяем объект. Здесь вы должны увидеть новое имя и возраст человека.

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

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

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

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