Menu
Coddy logo textTech

דפוס Decorator

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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 אונליין