Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

حماية الحالة

جزء من قسم البرمجة كائنية التوجه في رحلة 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 عندما يكون المبلغ أكبر من الرصيد. لا يتغير شيء عند رفع خطأ.

يقرأ الكود الم provided الأوامر 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 عبر الإنترنت