Приватные сеттеры
Часть раздела Объектно-ориентированное программирование путешествия по 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