Menu
CoddyTech

דפוס Decorator

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Ruby של Coddy. שיעור 47 מתוך 57.

תבנית הdecorator עוטפת אובייקט באובייקט אחר בעל אותו ממשק, וכך מוסיפה משהו לפני העברת הקריאה הלאה או אחריה. אפשר לשרשר עוטפים בכל סדר:

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 אונליין