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

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

جزء من قسم البرمجة كائنية التوجه في رحلة 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 عبر الإنترنت