Андрей Васильев
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При проектировании решения в объектно-ориентированном подходе сначала необходимо идентифицировать ключевые сущности
Для нашего случая выделим следующие сущности:
BookInStockBooksДля описания классов используется конструкция
Для создания объектов класса в Ruby используется метод new
::new есть у каждого классаВ примере выше мы создали 2 объекта класса ClassName и записали их в переменные object_one и object_two
Методу можно передать данные, которые будут использованы при инициализации объекта:
После создания каждого объекта Ruby инициализирует объект, вызывая метод initiailize и передавая ему параметры из конструктора
class BookInStock
  def initialize(isbn, price)
    @isbn = isbn
    @price = Float(price) # Может выбросить исключение
  end
end@p и pp показывают внутреннее состояние объектаputs пытается преобразовать объект к строкеПри преобразовании объекта к строке вызывается метод to_s, который можно переопределить для своего класса
to_s не принимает аргументовto_s должен вернуть строкуДля работы методов p и pp иногда разумно реализовать метод inspect, который формирует представление об объекте с точки зрения программиста
В языке Ruby есть методы-помощники, упрощающие задачу описания атрибута на основе переменной экземпляра
Метод attr_reader создаёт методы для получения значений переменных экземпляра
attr_reader — метод для чтения значения переменной, при этом переменная не становится публичнойМетоду можно передать несколько имён переменных, но это усложняет совместную работу при использовании систем контроля версий
При написании классов согласно методологии объектно-ориентированного программирования мы должны придерживаться принципа инкапсуляции, при котором данными объекта должен управлять только этот объект
Чтение атрибута — это получение ссылки на «внутренний» объект
Если данный объект можно изменить, тогда нельзя сказать, что класс с данным атрибутом полностью управляет своими данными
Обычным для объектно-ориентированных языков способом изменения атрибута является создание специального метода
=Метод attr_accessor создаёт методы для чтения и записи данных в переменные экземпляра
Метод attr_writer создаёт метод для записи данных. Зачастую не используется, так как сложно представить ситуацию, когда надо записывать данные, но не считывать их.
В Ruby вы всегда взаимодействуете с методами, а не с переменными экземпляра. Это позволяет зафиксировать интерфейс объекта и легко менять его реализацию в будущем
Можно описать атрибут, который выполняет более сложные действия по сравнению с чтением и записью
def price_in_copeks
  Integer(@price * 100 + 0.5)
end
def price_in_copeks=(copeks)
  @price = copeks / 100.0
endbook.price_in_copeksbook.price_in_copeks = 15Во время решения реальных задач с помощью классов мы описываем не только реальные объекты, но также и технические элементы, необходимые для достижения цели
В нашем приложении необходимо обрабатывать информацию о множестве книг, которая записана в CSV-файлы
Определим интерфейс класса, который мы хотим реализовать
read_in_csv_datatotal_value_in_stocknumber_of_each_isbnКласс Books должен считывать данные из нескольких CSV-файлов, которые собираются разными устройствами
csvCSV, позволяющий читать и записывать CSV-документыforeach"Date","ISBN","Price"
"2013-04-12","978-1-9343561-0-4",39.45Класс Books должен сохранять информацию о всех считанных книгах. Для её хранения будем использовать массив.
[]<< добавляет объект в конец массиваpush добавляет один или несколько объектов в конец массиваRuby позволяет разработчикам определить альтернативные имена для публичных методов. Для создания псевдонима следует использовать метод alias_method.
В Ruby 2.5 ввели альтернативное название для метода push — append
Обычно один исходный файл на языке Ruby содержит один класс или один модуль, что
Желательно отделять модули, ответственные за взаимодействие с внешним миром (пользователи, файлы) от модулей, которые реализуют обработку данных
Для подключения других файлов используются методы
require для подключения внешних библиотекrequire_relative для подключения собственных файлов по относительному путиОтносительный путь строится относительно текущего файла
Ruby предоставляет 3 уровня контроля доступа к методам
public) методы могут быть вызваны любым объектом
initialize являются публичнымиprotected) методы могут быть вызваны из любого объекта данного классаprivate) методы могут быть вызваны только лишь внутри данного классаОтличия от знакомых вам языков программирования
Для указания контроля доступа используются методы public, protected, private
person2 содержит копию данныхperson2 объект person1 не изменяетсяПри применении данной техники вместо изменения текущего объекта создаётся копия оригинального объекта
class Maslo
  attr_reader :weight # Только лишь чтение
  def initialize(weight)
    @weight = weight
  end
  def take(weight)
    new Maslo(@weight - weight) # Новый объект
  end
end