Справка
Телеграм чат начинающих программистов. Общаемся и помогаем друг другу
Если ссылка не открывается, можно найти нас в поиске по чатам @rubyrush
или
пойти другим путем
Сегодня мы научимся писать данные в файлы, узнаем как работать со временем в Ruby, напишем программу-дневник.
В программах мы постоянно оперируем объектами, мы уже говорили об этом в 4-м уроке: строки, числа, массивы.
Наши объекты хранятся с помощью переменных: неких ярлыков, которые позволяют обращаться к объектам по имени.
Вы уже знаете, что в Ruby есть много разных видов объектов: строки (String), целые числа (Fixnum), массивы (Array). Пришло время осознать, что этих типов гораздо больше: есть ещё файлы (File), ассоциативные массивы (Hash), метки (Symbol) даже моменты времени (Time) и даты (Date), а также много-много всего другого.
С чем-то мы познакомимся в этом блоке нашего курса, что-то ждёт нас в дальнейшем, но сейчас важно одно, что у каждого из этих объектов есть свой тип.
Типы объектов в программировании называются классами. Ruby не исключение.
Ruby вообще очень высокоразвитый язык, там любая закорючка — это объект какого-то класса. Но это так, лирическое отступление.
Напомню, что посмотреть класс любого объекта можно вызвав у этого объекта по цепочке методы .class
и .name
.
Что такое методы объектов вам станет понятно к концу этого урока, а пока просто напомним, что
puts "Я строка".class.name
выведет на экран String
, а
puts ["А","я","массив","строк"].class.name
выведет на экран Array
.
Класс — это некое описание типа объектов, которые можно создавать. Прежде чем человек создал первый паровоз, он как-то описал (на бумаге, в своем воображении, в чертежах) новое для того времени понятие «паровоз». Он наверняка придумал какими свойствами должен обладать паровоз, как он должен функционировать и так далее.
Другими словами он придумал новое понятие, новый тип объектов «паровоз». Программисты бы сказали — создал класс Паровоз
.
И уже потом человечество начало производить различные конкретные паровозы, создавать объекты этого класса.
То есть — прежде чем создавать какие-то объекты в вашей программе, Ruby должен знать их класс. А для этого нужно сперва объявить класс. Объявить класс это значит описать в программе, как должен класс называться и главное — какими свойствами и поведением он должен обладать.
До сих пор мы использовали встроенные в Ruby классы (строки, числа, массивы) – мы создавали объекты этих классов и с ними игрались. Нам не нужно было описывать эти классы, ведь они уже описаны в самом языке Ruby.
Что делать, если вам понадобился в программе новый тип объектов, которых нету в языке? А бывает так очень часто. Тогда вам нужно написать свой класс.
Давайте определимся с философским вопросом: «Когда нужно создавать новые классы». Сразу скажем, что понимание это приходит с опытом, поэтому не бойтесь экспериментировать и делать классы когда считаете это нужным.
Ругать вас за это никто не будет. Дадим лишь несколько советов, как понять, что ситуация требует именно нового класса, а не просто метода.
1. Если вы понимаете, что некую часть вашей программы можно выделить в независимый объект. Объект со своим собственным поведением и свойствами.
Главное, чтобы вы чётко понимали, как вы объясните другому человеку, что это за класс. Если вы можете сформулировать на русском языке название класса в виде простого слова — это хороший признак, что он заслуживает существования.
Например, для нашей виселицы из предыдущего урока можно было бы создать класс: «Печатальщик результата», который бы занимался всем, что связано с выводом информации для пользователя в консоль.
2. Если в языке программирования не предусмотрен какой-то уже имеющийся класс для вашей цели.
Чаще всего просто погуглив, вы либо найдёте нужный класс в Ruby, либо поймёте, как в этой ситуации поступают другие люди. Если же информацию быстро найти не удалось, смело делайте свой класс.
3. Если в вашей программе есть абстрактная модель чего-то.
Например, если вы пишете программу для управления библиотекой, то понятно, что вам нужен класс книги или даже класс стеллажа, может быть, понадобится класс автора и класс жанра.
Это всё определяется в момент проектирования программы, подробнее об этом процессе мы говорили в 10-м уроке.
Во-первых, классы солидны. То есть, класс представляет собой такой конкретный объёмный кусок программы. Часто случается так, что программисты в своей работе используют один и тот же класс в нескольких проектах.
Иногда классы даже включаются в структуру языка, как это стало с классами строки String
и момента времени Time
, настолько они удобные.
Классы принято описывать в отдельных файлах. Каждому классу — свой файл. Это существенно упрощает понимание программы.
Во-вторых, классы важны. Мы просто-таки запрещаем дорогим слушателям нашего курса создавать классы, называя их абы как. Придумайте своему классу осмысленное название, чтобы вы могли посреди ночи проснуться и по названию класса сказать, что он делает, хотя бы приблизительно.
Для примера создадим класс Мост
(Bridge
), который мы опишем в файле bridge.rb
(как обычно, положив его в новую папку c:\rubytut\lesson11
):
class Bridge
# Описание класса
end
Чтобы Ruby понял, что это не просто код, а описание класса, мы обернули его в конструкцию class-end
. Обратите внимание, что все имена классов в Ruby (в других языках тоже) обычно начинаются с большой буквы.
Если бы класс состоял из двух слов, то второе слово тоже было бы с большой буквы
class RoadBridge
...
end
Внутри конструкции class-end
мы пишем методы нашего класса. Как мы уже знаем, методы описываются с помощью конструкции def-end
.
Особое внимание следует обратить на метод initialize
— это так называемый конструктор класса, но об этом чуть позже.
Пока мы просто описали класс, ничего интересного не произойдёт. Нужно создать хотя бы один объект этого класса. Для этого нам в нашей основной программе doroga.rb
необходимо подключить файл bridge.rb
с описанием класса Bridge
.
Мы умеем делать это с помощью команды require
:
require "bridge.rb"
После этого можно создать новый объект нашего нового класса Bridge
. Для этого мы пишем
bridge = Bridge.new
Это очень важный момент! Давайте разберёмся, что значит каждое слово в этой записи.
Во-первых, что такое bridge
, оно написано маленькими буквами, значит это не класс, а объект, вернее переменная.
Во-вторых знак равно (=
), он означает, что мы в переменную bridge
хотим что-то записать, хотим, чтобы переменная bridge
указывал на то, что будет справа от знака равно.
В-третьих, мы видим название нашего нового класса Bridge
. Мы только что описали этот класс в отдельном файле bridge.rb
и подключили его (файл) с помощью команды require
.
Наконец, .new
— мы вызвали у нашего класса специальный метод, который как бы говорит классу: «О великий и могучий! Создай для нас свое земное воплощение в виде конкретного объекта!»
А класс отвечает: «Так и быть, я сжалюсь над тобой, смертный и дам тебе свой экземпляр, но при одном условии — я сразу же вызову у этой копии метод initialize
».
Поэтому метод .new
возвращает новый объект данного класса.
Причем при создании объекта у него вызывается специальный метод с именем initialize
. Такой метод в программировании называется конструктор.
class Bridge
def initialize
puts "Мост создан"
end
end
Вы можете объявить в классе такой метод и написать в нем какой-то функционал – тогда этот функционал будет выполнен один раз при создании каждого объекта этого класса. Но можно и не писать, тогда конструктор будет пустой, объект создастся без каких-то дополнительных действий.
Конкретный объект какого-то класса в программировании называется экземпляр класса. По-английски instance
. Запомните эти слова, вот увидите, они несут свет озарения в чистом виде! ;)
Конечно, всю эту драму придумали разработчики, чтобы было удобнее создавать новые классы. В методе initialize
, который вызывается каждый раз, когда создаётся новый объект указанного класса, описывается, что должно произойти с экземпляром класса перед тем, как он будет создан. Если это класс книги, например, то нужно заполнить её название и год издания. Может быть ещё имя и фамилию автора и жанр. Всё на усмотрение разработчика класса.
Ещё раз, объект (экземпляр класса) и класс — это разные вещи, как есть вот мы, «Миша» и «Вадим» — объекты, а есть «Человек» — класс, некий собирательный образ, абстракция для всех людей на Земле (и на её орбите, а возможно и в других галактиках).
Итак, мы создали новый экземпляр класса Bridge
и сделали так, что переменная bridge
указывает на этот объект.
Если мы напишем
puts bridge.class.name
То увидим название нашего класса Bridge
.
А теперь смертельный номер. Просьба всех слабонервных удалиться. Если всё в Ruby это экземпляр какого-то класса, то что же тогда такое этот наш Bridge
? Какого будет вам узнать, что это тоже объект! «Какой же у него класс?» — спросите вы. Посмотрите сами, вы уже не маленькие.
def open
puts "Мост открыт, можно ехать"
end
Внутри нашего класса Bridge
мы написали метод open
. Этот метод на самом деле есть не у самого класса, а именно у его экземпляра.
Для того, чтобы «открыть» мост (объект класса Bridge
), на который указывает переменная bridge
, нам необходимо вызвать у этого объекта метод open
. Это делается очень просто и изящно:
bridge.open
и мы увидим в консоли наш текст открытия моста:
Мост открыт, можно ехать!
Именно вызов метода экземпляра класса мы делали, когда вызывали у массива, например, метод to_s
:
array = [1,2,3]
puts array.to_s
выводит в консоль "[1, 2, 3]"
— мы вызываем у объекта array
(экземпляра класса Array
) метод to_s
, который возвращает этот массив но уже как строку (экземпляр класса String
).
В методы класса, как и в обычные методы можно передавать параметры, как и обычные методы, они возвращают (или нет) какие-то значения.
Единственное отличие этих методов, в том, что они привязаны к экземпляру класса и в этих методах в связи с этим доступны «поля класса» или «переменные экземпляра класса» или «переменные объекта». Такие переменные используются для хранения состояния экземпляра класса, его свойств.
Например, наш мост bridge
(экземпляр класса Bridge
) может быть каменным или деревянным, длинным или коротким, узким или широким, пешеходным или автомобильным (или даже железнодорожным) и так далее.
Давайте сделаем наш мост открывающимся и для этого создадим поле класса opened
(открыт). В руби поля класса начинаются с символа «собаки» — @
(чтобы не путались с методами), поэтому в конструкторе мы опишем поведение моста по умолчанию в таком виде:
def initialize
puts "Мост создан"
@opened = false
end
а в метод open
добавим изменение этого внутреннего поля на true
def open
@opened = true
puts "Мост открыт, можно ехать"
end
Все важные поля вашего объекта должны быть объявлены в конструкторе! Вам нужно сообщить Ruby заранее какими свойствами будут обладать объекты вашего класса.
Текущее значение всех полей какого-то объекта определяют так называемое состояние объекта. Фактически один объект отличается от другого объекта того же класса своим состоянием (один мост открыт, другой закрыт, например).
Мы также напишем новый метод 'is_opened?', который будет возвращать true
, если мост открыт и false
, если закрыт:
def is_opened?
return @opened
end
Программисты Ruby договорились между собой, что все методы, которые возвращают true
или false
, будут заканчиваться знаком вопроса. В других языках как правило знак вопроса не используют в названиях методов.
Обратите внимание, что мы никак не можем достучаться до поля класса из нашей программы, именно поэтому для каждого обращения к ней нам нужен отдельный метод (если это действительно необходимо делать из нашей программы).
В самой программе doroga.rb
мы теперь перепишем открытие моста только для случая, когда мост закрыт:
if !bridge.is_open?
bridge.open
end
После этого наш мост откроется и напишет Мост открыт, можно ехать!
.
Ещё раз обратим ваше внимание, что если мы создадим новый мост
another_bridge = Bridge.new
то этот новый мост будет закрыт. another_bridge.is_open?
вернёт false
.
Надо просто немного привыкнуть к этой концепции класс-объект. После небольшой практики вы будете в этом как рыба в воде.
Кстати, рыба и селедка — селедка это объект (если конкретная селедка, вот эта).
А просто "рыба" это уже класс ;)
В этом уроке мы познакомились с очень важным понятием классов. Узнали, что такое класс и чем он отличается от своего экземпляра. Поняли как, а главное зачем их создавать, как наполнять их методами, что такое методы класса, как их писать и использовать. Узнали также о полях класса и как в них хранятся свойства объекта, его состояние.
А на следующем уроке мы будем использовать классы в реальных задачах и перепишем с их помощью программу Виселица — сделаем ее более наглядной и привлекательной.