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
Для описания классов используется конструкция
class ClassName
# Содержимое класса
end
Для создания объектов класса в Ruby используется метод new
ClassName.new
object_one = ClassName.new object_two =
::new
есть у каждого классаВ примере выше мы создали 2 объекта класса ClassName
и записали их в переменные object_one
и object_two
Методу можно передать данные, которые будут использованы при инициализации объекта:
BookInStock.new('978-1-9343560-7-4', 10.2) book =
После создания каждого объекта Ruby инициализирует объект, вызывая метод initiailize
и передавая ему параметры из конструктора
class BookInStock
def initialize(isbn, price)
@isbn = isbn
@price = Float(price) # Может выбросить исключение
end
end
@
p
и pp
показывают внутреннее состояние объектаputs
пытается преобразовать объект к строке#<Object:0x000056409cbcf7e0>
При преобразовании объекта к строке вызывается метод to_s
, который можно переопределить для своего класса
to_s
не принимает аргументовto_s
должен вернуть строкуДля работы методов p
и pp
иногда разумно реализовать метод inspect
, который формирует представление об объекте с точки зрения программиста
class Book
..def isbn
@isbn
end
end
Book.new('AAA-53-555', 500)
book = puts book.isbn
В языке Ruby есть методы-помощники, упрощающие задачу описания атрибута на основе переменной экземпляра
Метод attr_reader
создаёт методы для получения значений переменных экземпляра
attr_reader :isbn
attr_reader :price
attr_reader
— метод для чтения значения переменной, при этом переменная не становится публичнойattr_reader :isbn, :price
Методу можно передать несколько имён переменных, но это усложняет совместную работу при использовании систем контроля версий
При написании классов согласно методологии объектно-ориентированного программирования мы должны придерживаться принципа инкапсуляции, при котором данными объекта должен управлять только этот объект
Чтение атрибута — это получение ссылки на «внутренний» объект
Если данный объект можно изменить, тогда нельзя сказать, что класс с данным атрибутом полностью управляет своими данными
Изменяемые объекты в Ruby
Неизменяемые объекты в Ruby
# frozen_string_literal: true
Обычным для объектно-ориентированных языков способом изменения атрибута является создание специального метода
public void setPrice(double newPrice) {
price = newPrice }
=
def isbn=(isbn)
@isbn = isbn
end
'978-1-9343561-0-4' book.isbn =
Метод attr_accessor
создаёт методы для чтения и записи данных в переменные экземпляра
attr_accessor :isbn
attr_accessor :price
Метод attr_writer
создаёт метод для записи данных. Зачастую не используется, так как сложно представить ситуацию, когда надо записывать данные, но не считывать их.
В Ruby вы всегда взаимодействуете с методами, а не с переменными экземпляра. Это позволяет зафиксировать интерфейс объекта и легко менять его реализацию в будущем
Можно описать атрибут, который выполняет более сложные действия по сравнению с чтением и записью
def price_in_copeks
Integer(@price * 100 + 0.5)
end
def price_in_copeks=(copeks)
@price = copeks / 100.0
end
book.price_in_copeks
book.price_in_copeks = 15
Во время решения реальных задач с помощью классов мы описываем не только реальные объекты, но также и технические элементы, необходимые для достижения цели
В нашем приложении необходимо обрабатывать информацию о множестве книг, которая записана в CSV-файлы
Определим интерфейс класса, который мы хотим реализовать
read_in_csv_data
total_value_in_stock
number_of_each_isbn
Класс Books
должен считывать данные из нескольких CSV-файлов, которые собираются разными устройствами
csv
CSV
, позволяющий читать и записывать CSV-документыforeach
CSV.foreach('file.csv', headers:true) do |row|
"#{row['ISBN']}, #{row['Price']}"
puts end
"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
для подключения собственных файлов по относительному путиОтносительный путь строится относительно текущего файла
'book'
require_relative 'util/timer'
require_relative '../book' require_relative
book
из файла main
timer
из файла main
book
из файла timer
Ruby предоставляет 3 уровня контроля доступа к методам
public
) методы могут быть вызваны любым объектом
initialize
являются публичнымиprotected
) методы могут быть вызваны из любого объекта данного классаprivate
) методы могут быть вызваны только лишь внутри данного классаОтличия от знакомых вам языков программирования
Для указания контроля доступа используются методы public
, protected
, private
class MyClass
private
def method_one
end
def method_two
end
end
class MyClass
:method_one, :method_two
pritave end
'Tim'
person1 =
person2 = person10] = 'J' # => 'Jim'
person1[0] # => 'Jim' person2[
'Tim'
person1 =
person2 = person1.dup0] = 'J' person1[
person2
содержит копию данныхperson2
объект person1
не изменяется'Tim'
person1 =
person2 = person1
person1.freeze0] = 'J' # => Ошибка изменения константы person2[
При применении данной техники вместо изменения текущего объекта создаётся копия оригинального объекта
class Maslo
attr_reader :weight # Только лишь чтение
def initialize(weight)
@weight = weight
end
def take(weight)
Maslo(@weight - weight) # Новый объект
new end
end