비공개 세터
Coddy Ruby 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 19번째.
대개 어떤 값은 누구나 읽을 수 있어야 하지만 객체만 변경할 수 있어야 합니다. 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.와 함께 호출됩니다. 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에 직접 할당하는 대신 private setter를 거치면 값이 유효한지 확인하는 검사와 같은 규칙을 한곳에서 관리할 수 있습니다:
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는 외부에서 읽을 수 있지만 private setter를 통해서만 설정할 수 있으며, 초기값은 0입니다. earn(amount)는 잔액에 금액을 더합니다. spend(amount)는 금액을 차감하고 true를 반환하며, 잔액이 부족할 때는 변경 없이 false를 반환합니다. history는 성공한 변경 횟수를 반환하며, 역시 private setter를 통해서만 설정할 수 있습니다.
제공된 코드는 +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 컴파일러