Menu
Coddy logo textTech

Wzorzec dekoratora

Część sekcji Programowanie obiektowe ścieżki Ruby w Coddy. Lekcja 47 z 57.

Wzorzec dekoratora opakowuje obiekt w inny obiekt z tym samym interfejsem, który dodaje coś przed przekazaniem wywołania dalej lub po nim. Dekoratory można łączyć w dowolnej kolejności:

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)

Wynik:

coffee, milk, milk: 3.00

Każdy dekorator przechowuje opakowany obiekt i przekazuje do niego wywołania, więc wspólna klasa bazowa może obsługiwać tę część. Każdy konkretny dekorator nadpisuje następnie tylko to, co zmienia:

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)

Wynik:

coffee, sugar: 2.20

Biblioteka standardowa SimpleDelegator (require 'delegate') przekazuje każdą metodę, której sama nie definiuje, opakowanemu obiektowi, dzięki czemu dekoratory pozostają krótkie, gdy interfejs jest rozbudowany:

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")

Wynik:

HELLO!
5
true

Dekoratory dodają zachowanie pojedynczym obiektom w czasie działania programu i można je dowolnie łączyć. W przypadku podklas każda kombinacja, taka jak MilkSugarCoffee, wymagałaby własnej klasy:

Sugar.new(Milk.new(Coffee.new))      # dowolna kombinacja, dowolna kolejność
Whip.new(Sugar.new(Coffee.new))

# z podklasami: MilkCoffee, SugarCoffee, MilkSugarCoffee, ...
challenge icon

Wyzwanie

Łatwy

Uzupełnij dekoratory kawy. Coffee kosztuje 2.00, a jego opis to coffee. Milk dodaje 0.50 i , milk, Sugar dodaje 0.20 i , sugar, a Whip dodaje 0.70 i , whip. Wszystkie trzy dekoratory współdzielą klasę bazową Extra, która przechowuje opakowany napój i przekazuje wywołania cost oraz description; każda podklasa używa super.

Każdy wiersz wejściowy to zamówienie, na przykład milk sugar. Dostarczony kod opakowuje nowy obiekt Coffee dekoratorami w tej kolejności i wypisuje coffee, milk, sugar: 2.70.

Twój kod umieść w plikach coffee.rb, extra.rb, milk.rb, sugar.rb i whip.rb. main.rb zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

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

# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Ruby online