Классы, объекты и переменные

Андрей Васильев

2020

Задача

Мы управляем магазином подержанных книг. Каждую неделю проводится инвентаризация. Работники сканируют бар-коды на книгах и сохраняют их в CSV-списки.

Пример файла

"Date","ISBN","Price"
"2019-04-12","978-1-9343561-0-4",939.45
"2019-04-13","978-1-9343561-6-6",645.67
"2019-04-14","978-1-9343560-7-4",836.95

Задачи системы

  • Выяснить количество книг каждого наименования
  • Подсчитать общую стоимость всех книг

Идентификация ключевых элементов

При проектировании решения в объектно-ориентированном подходе сначала необходимо идентифицировать ключевые сущности

Для нашего случая выделим следующие сущности:

  • Сущность, описывающая одну книгу, BookInStock
  • Коллекция книг, содержащихся в наличии, Books

Описание классов в Ruby

Для описания классов используется конструкция

Создание объектов в Ruby

Для создания объектов класса в Ruby используется метод new

  • Метод создаёт новый объект и возвращает ссылку на него
  • Метод принадлежит классу и вызывается на классе
  • Метод ::new есть у каждого класса

В примере выше мы создали 2 объекта класса ClassName и записали их в переменные object_one и object_two

Методу можно передать данные, которые будут использованы при инициализации объекта:

Инициализация объектов в Ruby

После создания каждого объекта Ruby инициализирует объект, вызывая метод initiailize и передавая ему параметры из конструктора

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

«Печать» объектов

  • Методы p и pp показывают внутреннее состояние объекта
  • Метод puts пытается преобразовать объект к строке
  • Стандартный способ представления: имя класса и уникальный идентификатор

Преобразование к строке

При преобразовании объекта к строке вызывается метод to_s, который можно переопределить для своего класса

  • Метод to_s не принимает аргументов
  • Метод to_s должен вернуть строку

Для работы методов p и pp иногда разумно реализовать метод inspect, который формирует представление об объекте с точки зрения программиста

Атрибуты объекта

  • Все переменные экземпляра являются приватными
  • Для доступа к значениям переменных экземпляра и изменения их состояния определяются методы
  • Такие методы формируют атрибут
  • Атрибуты описывают состояние объекта, видимое другими объектами приложения

Упрощённое создание атрибутов объектов

В языке Ruby есть методы-помощники, упрощающие задачу описания атрибута на основе переменной экземпляра

Метод attr_reader создаёт методы для получения значений переменных экземпляра

  • Аргументами метода являются символы, которые должны соответствовать именам переменных экземпляра
  • Результат работы метода attr_reader — метод для чтения значения переменной, при этом переменная не становится публичной

Методу можно передать несколько имён переменных, но это усложняет совместную работу при использовании систем контроля версий

Опасность публикации атрибутов на чтение

При написании классов согласно методологии объектно-ориентированного программирования мы должны придерживаться принципа инкапсуляции, при котором данными объекта должен управлять только этот объект

Чтение атрибута — это получение ссылки на «внутренний» объект

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

Изменяемые объекты в Ruby

  • Строка
  • Массив
  • Хеш

Неизменяемые объекты в Ruby

  • Числа
  • Строковые литералы

Изменение атрибутов

Обычным для объектно-ориентированных языков способом изменения атрибута является создание специального метода

  • В Ruby принято оформлять взаимодействие с атрибутами, как с обычными переменными
  • Для этого метод, устанавливающий значение атрибута, имеет на конце символ =

Методы для создания атрибутов

Метод attr_accessor создаёт методы для чтения и записи данных в переменные экземпляра

  • Аргументами метода являются символы, которые должны соответствовать именам переменных экземпляра
  • После использования метода переменную экземпляра может изменить любой внешний объект, таким образом данный объект теряет контроль над переменной

Метод attr_writer создаёт метод для записи данных. Зачастую не используется, так как сложно представить ситуацию, когда надо записывать данные, но не считывать их.

Виртуальные атрибуты

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

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

Виртуальный атрибут — стоимость книги в копейках

  • Чтение значения: book.price_in_copeks
  • Присваивание значения: book.price_in_copeks = 15

Взаимодействие между классами

Во время решения реальных задач с помощью классов мы описываем не только реальные объекты, но также и технические элементы, необходимые для достижения цели

В нашем приложении необходимо обрабатывать информацию о множестве книг, которая записана в CSV-файлы

Класс Books — чтение и обработка набора данных

Определим интерфейс класса, который мы хотим реализовать

  • Чтение информации из нескольких CVS-файлов
    • read_in_csv_data
  • Вычисление нужных характеристик
    • total_value_in_stock
    • number_of_each_isbn

Чтение данных из CSV-файла

Класс Books должен считывать данные из нескольких CSV-файлов, которые собираются разными устройствами

  • Стандартная поставка интерпретатора Ruby включает в себя библиотеку csv
  • Библиотека предоставляет класс CSV, позволяющий читать и записывать CSV-документы
  • Для чтения можно воспользоваться методом foreach
"Date","ISBN","Price"
"2013-04-12","978-1-9343561-0-4",39.45

Хранение информации о книгах

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

  • Пустые массивы обычно создаются с помощью литерала []
  • Метод << добавляет объект в конец массива
  • Метод push добавляет один или несколько объектов в конец массива

Альтернативные имена методов

Ruby позволяет разработчикам определить альтернативные имена для публичных методов. Для создания псевдонима следует использовать метод alias_method.

В Ruby 2.5 ввели альтернативное название для метода pushappend

Структурирование файлов приложения

Обычно один исходный файл на языке Ruby содержит один класс или один модуль, что

  • Позволяет легко находить файл с нужным классом
  • Облегчает рефакторинг исходного кода
  • Облегчает модульное тестирование

Желательно отделять модули, ответственные за взаимодействие с внешним миром (пользователи, файлы) от модулей, которые реализуют обработку данных

  • Основная задача первых состоит в проверке введённых данных, организации взаимодействия с пользователем
  • Задача вторых — выполнение нетривиальной обработки данных

Подключение внешних файлов

Для подключения других файлов используются методы

  • require для подключения внешних библиотек
  • require_relative для подключения собственных файлов по относительному пути

Относительный путь строится относительно текущего файла

Пример структуры ФС
Пример структуры ФС
  • Подключение файла book из файла main
  • Подключение файла timer из файла main
  • Подключение файла book из файла timer

Контроль доступа к методам класса

Ruby предоставляет 3 уровня контроля доступа к методам

  • Публичные (public) методы могут быть вызваны любым объектом
    • По умолчанию все методы кроме initialize являются публичными
  • Защищённые (protected) методы могут быть вызваны из любого объекта данного класса
  • Приватные (private) методы могут быть вызваны только лишь внутри данного класса

Отличия от знакомых вам языков программирования

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

Указание контроля доступа

Для указания контроля доступа используются методы public, protected, private

Указание уровня доступа для секции

Указание уровня доступа для конкретных методов

Переменные

  • Основная задача переменных — хранение ссылки на объект
  • Переменные не являются объектами
Связь переменных и объектов
Связь переменных и объектов
  • Оператор присваивания записывает ссылку на объект в переменную
  • Единое состояние объекта доступно из всех переменных, которые содержат в себе ссылку на данный объект

Предотвращение непродуманных изменений

Использование явного копирования

  • Объект person2 содержит копию данных
  • При изменении объекта person2 объект person1 не изменяется

Запрет всех последующих изменений

Проектирование объекта неизменяемым

При применении данной техники вместо изменения текущего объекта создаётся копия оригинального объекта

  • Удобно для многопоточного программирования
  • Внешний разработчик не может привести объект в некорректное состояние