プライベートセッター
CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 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 Walletprivate setter は、クラス内で self. を付けて呼び出します。これは Ruby が private メソッドに対して受け入れる唯一のレシーバーです。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を試みます。Rubyが拒否した場合はcannot set balanceを出力します。
コードは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
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rubyオンラインコンパイラ