Menu
Coddy logo textTech

הגנה על מצב האובייקט

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

אובייקט צריך לסרב לבקשות שישברו את המצב שלו. raise עוצר את המתודה לפני שמשהו משתנה. שם מחלקת השגיאה שלך מציין את הבעיה במדויק; < StandardError הופך את המחלקה לסוג של שגיאה (ירושה, פרק 5):

class InsufficientFunds < StandardError
end

class Account
  attr_reader :balance

  def initialize(balance)
    @balance = balance
  end

  def withdraw(amount)
    raise InsufficientFunds, "only #{balance} available" if amount > balance

    @balance -= amount
  end
end

account = Account.new(50)
begin
  account.withdraw(80)
rescue InsufficientFunds => e
  puts "#{e.class}: #{e.message}"
end
puts account.balance

פלט:

InsufficientFunds: only 50 available
50

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

class InvalidAmount < StandardError
end

class LimitReached < StandardError
end

class Card
  def charge(amount)
    raise InvalidAmount, "amount must be positive" unless amount.positive?
    raise LimitReached, "limit is 100" if amount > 100

    "charged #{amount}"
  end
end

[40, -5, 500].each do |amount|
  puts Card.new.charge(amount)
rescue InvalidAmount => e
  puts "fix the input: #{e.message}"
rescue LimitReached => e
  puts "try later: #{e.message}"
end

פלט:

charged 40
fix the input: amount must be positive
try later: limit is 100

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

class Point
  attr_accessor :x

  def initialize(x)
    @x = x
  end
end

pt = Point.new(1).freeze
puts pt.frozen?
begin
  pt.x = 2
rescue FrozenError => e
  puts e.class
end
puts pt.x

פלט:

true
FrozenError
1

קורא שמחזיר מערך פנימי מוסר את המערך האמיתי, כך שקוד חיצוני יכול לשנות אותו. החזר עותק קפוא באמצעות @items.dup.freeze, או באמצעות @items.dup, כדי שהאובייקט ישמור על השליטה בנתונים שלו:

class Playlist
  def initialize
    @songs = []
  end

  def add(song)
    @songs << song
  end

  def songs
    @songs.dup.freeze
  end
end

list = Playlist.new
list.add("Intro")
begin
  list.songs << "Hack"
rescue FrozenError
  puts "songs are read-only"
end
p list.songs

פלט:

songs are read-only
["Intro"]
challenge icon

אתגר

קל

הכרז על מחלקות השגיאה InvalidAmount ו-InsufficientFunds, שתיהן תת-מחלקות של StandardError, והשלם את Account. המתודות deposit(amount) ו-withdraw(amount) מעלות את InvalidAmount עם amount must be positive עבור סכומים קטנים מ-1. המתודה withdraw מעלה את InsufficientFunds עם balance is only 20 כאשר הסכום גדול מהיתרה. שום דבר לא משתנה כאשר מועלת שגיאה.

הקוד שסופק קורא פקודות dep n או wd n ומדפיס balance 70 אחרי כל אחת מהן, invalid: amount must be positive עבור InvalidAmount ו-refused: balance is only 20 עבור InsufficientFunds.

הקוד שלך צריך להיכתב בקבצים invalid_amount.rb, insufficient_funds.rb ו-account.rb. הקובץ main.rb מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.

נסו בעצמכם

require_relative 'invalid_amount'
require_relative 'insufficient_funds'
require_relative 'account'

# קוד קלט/פלט שסופק: יש להשאיר אותו כפי שהוא
input = []
while (line = gets)
  input << line.chomp
end
account = Account.new
input.each do |line|
  action, amount = line.split
  begin
    action == "dep" ? account.deposit(amount.to_i) : account.withdraw(amount.to_i)
    puts "balance #{account.balance}"
  rescue InvalidAmount => e
    puts "invalid: #{e.message}"
  rescue InsufficientFunds => e
    puts "refused: #{e.message}"
  end
end
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Ruby אונליין