Menu
Coddy logo textTech

데코레이터 패턴

Coddy Ruby 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 47번째.

데코레이터 패턴은 객체를 동일한 인터페이스를 가진 다른 객체로 감싸고, 호출을 전달하기 전이나 후에 무언가를 추가합니다. 데코레이터는 어떤 순서로든 중첩할 수 있습니다:

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 컴파일러