Приватные сеттеры
Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 19 из 57.
Часто значение должно быть доступно для чтения всем, но изменяться только объектом. attr_reader предоставляет публичный метод чтения, а private attr_writer — метод установки значения, которым может пользоваться только объект. Это работает, потому что attr_writer возвращает имена определённых им методов:
class Wallet
attr_reader :balance
def initialize
self.balance = 0
end
def earn(amount)
self.balance = balance + amount
end
private attr_writer :balance
end
w = Wallet.new
w.earn(30)
puts w.balance
begin
w.balance = 1_000_000
rescue NoMethodError => e
puts e.message
endВывод:
30
private method `balance=' called for an instance of WalletК приватному сеттеру внутри класса обращаются с помощью self. — это единственный получатель, которого Ruby допускает для приватных методов. Без self. присваивание просто создало бы локальную переменную:
class Lamp
attr_reader :on
def initialize
self.on = false
end
def toggle
self.on = !on
self
end
private
attr_writer :on
end
puts Lamp.new.toggle.toggle.toggle.onВывод:
trueИспользование приватного сеттера вместо непосредственного присваивания @balance позволяет сосредоточить правила в одном месте, например проверку допустимости значения:
class Tank
attr_reader :litres
def initialize(capacity)
@capacity = capacity
self.litres = 0
end
def fill(amount)
self.litres = litres + amount
end
def drain(amount)
self.litres = litres - amount
end
private
def litres=(value)
@litres = value.clamp(0, @capacity)
end
end
t = Tank.new(50)
t.fill(80)
puts t.litres
t.drain(70)
puts t.litresВывод:
50
0private attr_accessor :history скрывает и метод чтения, и сеттер, чтобы внешний код даже не видел внутреннее состояние:
class Editor
def initialize
self.history = []
end
def type(text)
history << text
end
private attr_accessor :history
endЗадание
ЛегкоЗаверши класс Wallet. Значение balance доступно для чтения извне, но устанавливается только через приватный сеттер; начальное значение — 0. Метод earn(amount) увеличивает его. Метод spend(amount) уменьшает его и возвращает true либо возвращает false, не изменяя значение, если баланса недостаточно. history возвращает количество успешных изменений; это значение также защищено приватным сеттером.
Предоставленный код считывает команды +n или -n, после каждой выводя balance 30, или declined, если расходование не удалось. Затем он выводит changes: 2 и пытается выполнить wallet.balance = 1000000, выводя cannot set balance, когда Ruby отказывает в этом.
Твой код размести в wallet.rb. В main.rb находится предоставленный код ввода/вывода, который нельзя редактировать.
Попробуйте сами
require_relative 'wallet'
# Предоставленный код ввода-вывода: оставьте его без изменений
input = []
while (line = gets)
input << line.chomp
end
wallet = Wallet.new
input.each do |cmd|
amount = cmd[1..].to_i
if cmd.start_with?("+")
wallet.earn(amount)
puts "balance #{wallet.balance}"
elsif wallet.spend(amount)
puts "balance #{wallet.balance}"
else
puts "declined"
end
end
puts "changes: #{wallet.history}"
begin
wallet.balance = 1_000_000
puts "balance set"
rescue NoMethodError
puts "cannot set balance"
end
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Классы и объекты
Работа с файламиОпределение классовПеременные экземпляраМетоды и selfto_s и inspectПовторение — читательский билет4Инкапсуляция
Приватные методыЗащищённые методыПриватные сеттерыЗащита состоянияПовторение — торговый автомат7Полиморфизм и утиная типизация
ПолиморфизмУтиная типизацияАбстрактные методыcase и классыПовторение — способы оплаты10Шаблоны проектирования, часть 2
Делегирование с помощью ForwardableШаблон декоратораШаблон строителяШаблон состояния2Аксессоры и self
Чтение и записьПользовательские сеттерыself в методахИдентичность и равенствоПовторение — список контактов5Наследование
ПодклассыПереопределение методовВызов superis_a? и предкиПовторение — расчёт зарплаты сотрудников8Специальные методы
Равенство и хешированиеМетоды операторовИндексация и добавлениеПользовательские коллекцииПовторение — дроби11Проект: Управление библиотекой
Книги и читателиВыдача книг3Методы и переменные классов
Методы классовПеременные экземпляра классаКонстантыФабричные методыПовторение — коды купонов6Модули и примеси
Модули и includeРасширение объектовМодуль ComparableМодуль EnumerableПовторение — спортивная лига9Паттерны проектирования. Часть 1
Паттерн «Одиночка»Фабричный паттернПаттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор Ruby