Menu
Coddy logo textTech

プロテクトメソッド

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 とも連携します。これは、たとえば2つのオブジェクトのどちらが勝つかを決めるために、通常、1つのクラスのオブジェクト間だけで値を共有する方法です:

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オンラインコンパイラ