Menu
CoddyTech

نمط المزيّن

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

يقوم نمط المُزَيِّن بتغليف كائن داخل كائن آخر له الواجهة نفسها، ما يضيف شيئًا قبل تمرير الاستدعاء أو بعده. ويمكن تكديس المُزَيِّنات بأي ترتيب:

class Coffee
  def cost
    2.0
  end

  def description
    "coffee"
  end
end

class Milk
  def initialize(drink)
    @drink = drink
  end

  def cost
    @drink.cost + 0.5
  end

  def description
    "#{@drink.description}, milk"
  end
end

order = Milk.new(Milk.new(Coffee.new))
puts format("%s: %.2f", order.description, order.cost)

الناتج:

coffee, milk, milk: 3.00

يخزّن كل مُزيّن الكائنَ الذي يغلّفه ويفوّض إليه، لذا يمكن لصنف أساسي مشترك أن يتولى هذا الجزء. ثم يتجاوز كل مُزيّن ملموس فقط ما يغيّره:

class Coffee
  def cost
    2.0
  end

  def description
    "coffee"
  end
end

class Extra
  def initialize(drink)
    @drink = drink
  end

  def cost
    @drink.cost
  end

  def description
    @drink.description
  end
end

class Sugar < Extra
  def cost
    super + 0.2
  end

  def description
    super + ", sugar"
  end
end

order = Sugar.new(Coffee.new)
puts format("%s: %.2f", order.description, order.cost)

الناتج:

coffee, sugar: 2.20

تُمرِّر المكتبة القياسية SimpleDelegator (require 'delegate') كلَّ طريقة لا تعرّفها إلى الكائن المُغلَّف، مما يُبقي الزخارف قصيرة عندما تكون الواجهة كبيرة:

require 'delegate'

class Loud < SimpleDelegator
  def to_s
    super.upcase + "!"
  end
end

text = Loud.new("hello")
puts text
puts text.size
puts text.start_with?("he")

الناتج:

HELLO!
5
true

تضيف المُزيّنات سلوكًا إلى كائنات مفردة وقت التشغيل، ويمكن دمجها بحرية. أما مع الفئات الفرعية، فستحتاج كل مجموعة، مثل MilkSugarCoffee، إلى فئة خاصة بها:

Sugar.new(Milk.new(Coffee.new))      # أي تركيبة، بأي ترتيب
Whip.new(Sugar.new(Coffee.new))

# باستخدام الفئات الفرعية: MilkCoffee, SugarCoffee, MilkSugarCoffee, ...
challenge icon

التحدي

سهل

أكمل مُزيّنات القهوة. تكلفة Coffee هي 2.00 ووصفها هو coffee. تضيف Milk مقدار 0.50 و, milk، وتضيف Sugar مقدار 0.20 و, sugar، وتضيف Whip مقدار 0.70 و, whip. تشترك المُزيّنات الثلاثة في الصنف الأساسي Extra، الذي يخزّن المشروب المُغلّف ويمرّر cost وdescription؛ ويستخدم كل صنف فرعي super.

كل سطر إدخال هو طلب، مثل milk sugar. يغلّف الكود المُرفق كائنًا جديدًا من Coffee بالمُزيّنات حسب ذلك الترتيب، ثم يطبع coffee, milk, sugar: 2.70.

ضع الكود في coffee.rb وextra.rb وmilk.rb وsugar.rb وwhip.rb. يحتوي main.rb على كود الإدخال والإخراج المُرفق، ولا يمكن تعديله.

جرّب بنفسك

require_relative 'coffee'
require_relative 'extra'
require_relative 'milk'
require_relative 'sugar'
require_relative 'whip'

# كود الإدخال/الإخراج المقدم: أبقه كما هو
input = []
while (line = gets)
  input << line.chomp
end
extras = { "milk" => Milk, "sugar" => Sugar, "whip" => Whip }
input.each do |line|
  drink = line.split.reduce(Coffee.new) { |d, name| extras[name].new(d) }
  puts format("%s: %.2f", drink.description, drink.cost)
end
quiz iconاختبر نفسك

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

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

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