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

نمط المزيّن

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

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

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 عبر الإنترنت