حماية الحالة
جزء من قسم البرمجة كائنية التوجه في رحلة 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 100freeze يجعل الكائن للقراءة فقط: بعد استخدامه، يؤدي أي تغيير في متغيرات حالته إلى رفع 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"]التحدي
سهلصرّح بصنفَي الخطأ 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
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1الفئات والكائنات
العمل مع الملفاتتعريف الفئاتمتغيرات النسخةالأساليب و selfto_s و inspectمراجعة - بطاقة المكتبة7تعدد الأشكال والكتابة بأسلوب البطة
تعدد الأشكالالكتابة بأسلوب البطةالأساليب المجردةالحالات والفئاتمراجعة - طرق الدفع3أساليب ومتغيرات الأصناف
أساليب الأصنافمتغيرات كائنات الأصنافالثوابتأساليب الإنشاءمراجعة - رموز القسائمتدرّب بنفسك: مترجم Ruby عبر الإنترنت