Menu
Coddy logo textTech

비공개 세터

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 Wallet

private 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
0

private attr_accessor :history는 리더와 세터를 모두 숨기므로, 외부 코드에서조차 볼 수 없어야 하는 내부 상태에 적합합니다:

class Editor
  def initialize
    self.history = []
  end

  def type(text)
    history << text
  end

  private attr_accessor :history
end
challenge icon

챌린지

쉬움

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
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Ruby 컴파일러