Menu
CoddyTech

المعيّنات الخاصة

جزء من قسم البرمجة كائنية التوجه في رحلة Ruby على Coddy. الدرس 19 من 57.

غالبًا ما ينبغي أن تكون القيمة قابلة للقراءة من الجميع، لكن لا يمكن تغييرها إلا بواسطة الكائن. يوفّر attr_reader قارئًا عامًا، بينما يوفّر private attr_writer أداة ضبط لا يمكن استخدامها إلا بواسطة الكائن. ينجح هذا لأن attr_writer يعيد أسماء الطرق التي يعرّفها:

class Wallet
  attr_reader :balance

  def initialize
    self.balance = 0
  end

  def earn(amount)
    self.balance = balance + amount
  end

  private attr_writer :balance
end

w = Wallet.new
w.earn(30)
puts w.balance
begin
  w.balance = 1_000_000
rescue NoMethodError => e
  puts e.message
end

الناتج:

30
private method `balance=' called for an instance of Wallet

يُستدعى المُعيِّن الخاص باستخدام self. داخل الصنف، وهو المُستقبِل الوحيد الذي يقبله Ruby للطرائق الخاصة. من دون self.، لن يُنشئ الإسناد سوى متغيّر محلي:

class Lamp
  attr_reader :on

  def initialize
    self.on = false
  end

  def toggle
    self.on = !on
    self
  end

  private

  attr_writer :on
end

puts Lamp.new.toggle.toggle.toggle.on

الناتج:

true

إن استخدام مُعيِّن خاص بدلًا من إسناد قيمة إلى @balance مباشرةً يُبقي القواعد في مكان واحد، مثل التحقق من صلاحية القيمة:

class Tank
  attr_reader :litres

  def initialize(capacity)
    @capacity = capacity
    self.litres = 0
  end

  def fill(amount)
    self.litres = litres + amount
  end

  def drain(amount)
    self.litres = litres - amount
  end

  private

  def litres=(value)
    @litres = value.clamp(0, @capacity)
  end
end

t = Tank.new(50)
t.fill(80)
puts t.litres
t.drain(70)
puts t.litres

الناتج:

50
0

يُخفي private attr_accessor :history كلاً من القارئ والمُعيِّن، وذلك للحالة الداخلية التي لا ينبغي حتى للكود الخارجي الاطّلاع عليها:

class Editor
  def initialize
    self.history = []
  end

  def type(text)
    history << text
  end

  private attr_accessor :history
end
challenge icon

التحدي

سهل

أكمل الصنف Wallet. يمكن قراءة balance من الخارج، لكن لا يمكن تعيينها إلا عبر أداة تعيين خاصة، وتبدأ قيمتها عند 0. تضيف earn(amount) المقدار إليها. تطرح spend(amount) المقدار وتُرجع true، أو تُرجع false دون إجراء أي تغيير عندما يكون الرصيد منخفضًا جدًا. تُرجع history عدد التغييرات الناجحة، وهي أيضًا محمية بأداة تعيين خاصة.

يقرأ الكود المرفق الأوامر +n أو -n، ويطبع balance 30 بعد كل أمر، أو declined عند فشل عملية الإنفاق. ثم يطبع changes: 2 ويحاول تنفيذ wallet.balance = 1000000، ويطبع cannot set balance عندما يرفض Ruby ذلك.

ضع الكود في wallet.rb. يحتوي main.rb على كود الإدخال والإخراج المرفق، ولا يمكن تعديله.

جرّب بنفسك

require_relative 'wallet'

# شيفرة الإدخال/الإخراج المقدَّمة: اتركها كما هي
input = []
while (line = gets)
  input << line.chomp
end
wallet = Wallet.new
input.each do |cmd|
  amount = cmd[1..].to_i
  if cmd.start_with?("+")
    wallet.earn(amount)
    puts "balance #{wallet.balance}"
  elsif wallet.spend(amount)
    puts "balance #{wallet.balance}"
  else
    puts "declined"
  end
end
puts "changes: #{wallet.history}"
begin
  wallet.balance = 1_000_000
  puts "balance set"
rescue NoMethodError
  puts "cannot set balance"
end
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Ruby عبر الإنترنت