Menu
CoddyTech

التفويض باستخدام Forwardable

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

يعني التفويض أن يوكِل كائنٌ بعضَ أعماله إلى كائنٍ يحتفظ به، بدلًا من أن يرث منه. قائمة التشغيل التي تحتوي على مصفوفة من الأغاني أكثر أمانًا من قائمة تكون هي نفسها مصفوفة، لأن الشيفرة الخارجية لا تحصل إلا على الطرق التي تختار تمريرها:

class Playlist
  def initialize
    @songs = []
  end

  def add(song)
    @songs << song
    self
  end

  def size
    @songs.size
  end

  def first
    @songs.first
  end
end

list = Playlist.new.add("Intro").add("Dune")
puts list.size
puts list.first

الناتج:

2
Intro

تكرار كتابة كل طريقة توجيه يدويًا أمر ممل. تتولى المكتبة القياسية Forwardable ذلك: استخدم extend Forwardable، ثم يوجّه def_delegators :@songs, :size, :first هذه الاستدعاءات إلى الكائن الموجود في @songs:

require 'forwardable'

class Playlist
  extend Forwardable
  def_delegators :@songs, :size, :first, :last, :empty?, :include?

  def initialize
    @songs = []
  end

  def add(song)
    @songs << song
    self
  end
end

list = Playlist.new.add("Intro").add("Dune")
puts "#{list.size} #{list.first} #{list.last}"
puts list.include?("Dune")
puts list.empty?

الناتج:

2 Intro Dune
true
false

def_delegator :@songs, :push, :add يمرّر طريقة واحدة تحت اسم جديد. يحتفظ الكائن بالتحكم الكامل في كل ما لا يمرّره، مثل delete أو clear:

require 'forwardable'

class Queue2
  extend Forwardable
  def_delegator :@jobs, :push, :enqueue
  def_delegator :@jobs, :shift, :next_job
  def_delegators :@jobs, :size

  def initialize
    @jobs = []
  end
end

q = Queue2.new
q.enqueue("a")
q.enqueue("b")
puts q.next_job
puts q.size
puts q.respond_to?(:clear)

الناتج:

a
1
false

فضّل التفويض («لديه») عندما يحتاج الصنف إلى جزء فقط من سلوك صنف آخر. فالوراثة من Array ستُتيح عشرات الأساليب، مثل clear وdelete_at، التي قد تُخلّ بقواعد قائمة التشغيل:

class Playlist < Array      # كل أساليب Array عامة، بما فيها clear
end

class Playlist              # فقط ما تفوّضه يكون عامًا
  extend Forwardable
  def_delegators :@songs, :size, :each
end
challenge icon

التحدي

سهل

أكمل الصنف Playlist باستخدام Forwardable. يحتفظ بالأغاني في @songs، وكل أغنية زوج من الشكل [title, seconds]. مرّر استدعاءات size وempty? وfirst باستخدام def_delegators، ومرّر استدعاء push باسم add باستخدام def_delegator. تعيد طريقته الخاصة duration المدة الإجمالية بالتنسيق m:ss، وتعيد titles العناوين مجتمعةً باستخدام , .

يضيف الكود المرفق أغنيةً واحدةً لكل سطر إدخال (title,seconds) باستخدام add، ثم يطبع الحجم، والعنوان الأول، والعناوين، والمدة، وما إذا كانت قائمة التشغيل تستجيب إلى clear.

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

جرّب بنفسك

require_relative 'playlist'

# كود الإدخال والإخراج المقدَّم: اتركه كما هو
input = []
while (line = gets)
  input << line.chomp
end
list = Playlist.new
input.each do |line|
  title, seconds = line.split(",")
  list.add([title, seconds.to_i])
end
puts "songs: #{list.size}"
puts "first: #{list.empty? ? "none" : list.first[0]}"
puts "titles: #{list.titles}"
puts "duration: #{list.duration}"
puts "can clear: #{list.respond_to?(:clear)}"
quiz iconاختبر نفسك

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

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

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