Menu
CoddyTech

プロテクトメソッド

CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 18/57。

protected メソッドは public と private の中間に位置します。外部のコードから呼び出すことはできませんが、同じクラスのオブジェクトなら、別のオブジェクトに対して呼び出すことができます。これにより、2つのオブジェクトはプログラムのほかの部分には公開しないデータを比較できます。

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def richer_than?(other)
    balance > other.balance
  end

  protected

  def balance
    @balance
  end
end

ada = Account.new("Ada", 500)
bo = Account.new("Bo", 300)
puts ada.richer_than?(bo)
begin
  ada.balance
rescue NoMethodError => e
  puts e.message
end

出力:

true
protected method `balance' called for an instance of Account

レシーバーが別のオブジェクトの場合に、privateとの違いが現れます。other.balanceはprotectedメソッドでは動作しますが、privateメソッドでは失敗します:

class Sample
  def initialize(n)
    @n = n
  end

  def compare_protected(other)
    other.guarded + guarded
  end

  def compare_private(other)
    other.hidden + hidden
  rescue NoMethodError
    "private refused other.hidden"
  end

  protected

  def guarded
    @n
  end

  private

  def hidden
    @n
  end
end

a = Sample.new(1)
b = Sample.new(2)
puts a.compare_protected(b)
puts a.compare_private(b)

出力:

3
private refused other.hidden

protectedはattr_readerでも使えます。たとえば、どちらのオブジェクトが勝つかを決めるために、同じクラスのオブジェクト間だけで値を共有する一般的な方法です。

class Player
  attr_reader :name

  def initialize(name, rating)
    @name = name
    @rating = rating
  end

  def stronger?(other)
    rating > other.rating
  end

  protected

  attr_reader :rating
end

a = Player.new("Ada", 1200)
b = Player.new("Bo", 1100)
puts a.stronger?(b)
puts [a, b].select { |pl| pl.stronger?(b) }.map(&:name).inspect

出力:

true
["Ada"]

機能する範囲で最も限定的なレベルを選びましょう。publicはクラスのインターフェース用、protectedはクラスのオブジェクト同士が比較するデータ用、privateはそれ以外すべて用です:

public      # 誰でも: order.total
protected   # そのクラスのオブジェクト: Account 内の other.balance
private     # そのオブジェクト自身のみ: subtotal, self.subtotal
challenge icon

チャレンジ

簡単

クラス Account を完成させてください。残高は他のアカウントからのみ読み取れるようにします。読み取りメソッドを protected にしてください。richer_than?(other) は残高を比較し、transfer_to(other, amount) は相手のアカウントの protected メソッド receive(amount) を通じて別のアカウントにお金を移し、true を返します。残高が不足している場合は false を返します。to_s は所有者の名前だけを返します。

提供されたコードは2人の所有者と残高を読み取り、その後 transfer a b 50 コマンドを処理して、ok または refused を出力します。続いて Ada is richer(または Bo is richer、または equal)を出力し、最後に外部から balance を読み取ろうとし、Ruby が拒否した場合は balance is protected を出力します。

コードは account.rb に記述してください。main.rb には提供された入出力コードがあり、編集できません。

自分で試してみよう

require_relative 'account'

# 提供された入出力コード: そのままにしてください
input = []
while (line = gets)
  input << line.chomp
end
accounts = input[0..1].map do |line|
  owner, balance = line.split
  Account.new(owner, balance.to_i)
end
a, b = accounts
input.drop(2).each do |line|
  _, from, to, amount = line.split
  source = from == "a" ? a : b
  target = to == "a" ? a : b
  puts source.transfer_to(target, amount.to_i) ? "ok" : "refused"
end
if a.richer_than?(b) then puts "#{a} is richer"
elsif b.richer_than?(a) then puts "#{b} is richer"
else puts "equal"
end
begin
  a.balance
  puts "balance is public"
rescue NoMethodError
  puts "balance is protected"
end
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rubyオンラインコンパイラ