Menu
Coddy logo textTech

מתודות מוגנות

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Ruby של Coddy. שיעור 18 מתוך 57.

מתודה מוגנת נמצאת בין ציבורית לפרטית. קוד חיצוני לא יכול לקרוא לה, אבל כל אובייקט מאותה מחלקה יכול לקרוא לה עבור אובייקט אחר. כך שני אובייקטים יכולים להשוות נתונים שהם לא חושפים לשאר התוכנית:

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 עובד עבור מתודה מוגנת ונכשל עבור מתודה פרטית:

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   # אובייקטים של המחלקה: other.balance בתוך Account
private     # רק האובייקט עצמו: subtotal, self.subtotal
challenge icon

אתגר

קל

השלם את המחלקה Account. אפשר לקרוא את היתרה רק מתוך חשבונות אחרים: הגדר את הקורא שלה כמוגן. richer_than?(other) משווה בין היתרות, transfer_to(other, amount) מעביר כסף לחשבון אחר באמצעות המתודה המוגנת receive(amount) של החשבון האחר ומחזיר true, או מחזיר false כשהיתרה נמוכה מדי. to_s מחזירה רק את שם הבעלים.

הקוד שסופק קורא את שמות שני הבעלים ואת היתרות, ואז קורא פקודות transfer a b 50 ומדפיס ok או refused. לאחר מכן הוא מדפיס Ada is richer (או Bo is richer, או equal) ולבסוף מנסה לקרוא את balance מבחוץ, ומדפיס balance is protected כש-Ruby מסרבת.

הקוד שלך ייכנס ל-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 אונליין