Menu
Coddy logo textTech

전략 패턴

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

전략 패턴은 알고리즘의 한 부분을 자체 객체로 분리하고 주 객체가 그 객체에 작업을 위임하도록 합니다. 전략을 교체하면 주 클래스를 변경하지 않고도 동작을 바꿀 수 있습니다:

class Standard
  def cost(kg)
    5 + kg * 1.5
  end
end

class Express
  def cost(kg)
    15 + kg * 3
  end
end

class Shipment
  attr_writer :strategy

  def initialize(kg, strategy)
    @kg = kg
    @strategy = strategy
  end

  def cost
    @strategy.cost(@kg)
  end
end

s = Shipment.new(2, Standard.new)
puts s.cost
s.strategy = Express.new
puts s.cost

출력:

8.0
21

전략들은 메서드 이름만 공유하므로 cost가 있는 객체라면 무엇이든 작동합니다. 다시 말해 덕 타이핑입니다. 새로운 배송 방식을 추가한다는 것은 Shipment 내부에 또 다른 분기를 추가하는 것이 아니라 클래스 하나를 추가하는 것을 의미합니다:

class Pickup
  def cost(_kg)
    0
  end
end

shipment.strategy = Pickup.new     # Shipment는 변경되지 않음

Ruby에서 전략은 흔히 람다입니다. call에 응답하는 것이라면 무엇이든 저장하고 호출할 수 있습니다. 람다, 메서드 객체 또는 직접 만든 클래스의 객체인지 여부와 관계없습니다:

class TextFormatter
  def initialize(strategy)
    @strategy = strategy
  end

  def format_all(words)
    words.map { |w| @strategy.call(w) }.join(" ")
  end
end

class Censor
  def call(word)
    word.size > 3 ? "*" * word.size : word
  end
end

words = %w[ruby is great]
puts TextFormatter.new(->(w) { w.upcase }).format_all(words)
puts TextFormatter.new(:capitalize.to_proc).format_all(words)
puts TextFormatter.new(Censor.new).format_all(words)

출력:

RUBY IS GREAT
Ruby Is Great
**** is *****

프로그램이 실행되는 동안 하나의 객체가 동작을 변경해야 하거나 변형이 단일 단계인 경우에는 서브클래스보다 전략을 선택하세요. 서브클래스는 객체의 전체 수명 동안 동작을 고정합니다:

# 서브클래싱: 생성 시 고정
ExpressShipment.new(2)

# 전략: 나중에 변경 가능
shipment = Shipment.new(2, Standard.new)
shipment.strategy = Express.new
challenge icon

챌린지

쉬움

배송 전략과 ShippingCalculator를 완성하세요. Standard#cost(kg)는 5에 kg당 1.50을 더한 값이고, Express#cost(kg)는 15에 kg당 3을 더한 값이며, Pickup#cost(kg)는 0입니다. 계산기는 전략과 함께 생성되며 strategy=를 사용해 전략을 변경할 수 있고, quote(kg)는 전략의 클래스 이름을 사용하여 2.5 kg by Express: 22.50을 반환합니다.

제공된 코드는 Standard로 시작하며 use express, use pickup, use standard 또는 quote 2.5 명령을 읽고 모든 견적을 출력합니다.

코드는 standard.rb, express.rb, pickup.rb 및 shipping_calculator.rb에 작성합니다. main.rb에는 제공된 입력/출력 코드가 들어 있으며 수정할 수 없습니다.

직접 해보기

require_relative 'standard'
require_relative 'express'
require_relative 'pickup'
require_relative 'shipping_calculator'

# 제공된 입력/출력 코드: 있는 그대로 유지하세요
input = []
while (line = gets)
  input << line.chomp
end
strategies = { "standard" => Standard, "express" => Express, "pickup" => Pickup }
calc = ShippingCalculator.new(Standard.new)
input.each do |line|
  cmd, arg = line.split
  if cmd == "use"
    calc.strategy = strategies[arg].new
  else
    puts calc.quote(arg.to_f)
  end
end
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Ruby 컴파일러