상태 패턴
Coddy Ruby 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 49번째.
상태 패턴은 객체의 현재 상태에 따라 달라지는 동작을 별도의 상태 객체로 옮깁니다. 주요 객체인 컨텍스트는 하나의 상태를 보유하고 호출을 해당 상태로 전달합니다. 상태는 응답하고 다음에 올 상태를 결정합니다:
class Red
def next_state
Green.new
end
def to_s
"red"
end
end
class Green
def next_state
Yellow.new
end
def to_s
"green"
end
end
class Yellow
def next_state
Red.new
end
def to_s
"yellow"
end
end
class TrafficLight
def initialize
@state = Red.new
end
def change
@state = @state.next_state
self
end
def to_s
"light is #{@state}"
end
end
light = TrafficLight.new
puts light
puts light.change
puts light.change.change출력:
light is red
light is green
light is red패턴이 없으면 컨텍스트의 모든 메서드에 상태 값에 대한 case가 필요하고, 새로운 상태가 추가될 때마다 모든 메서드를 수정해야 합니다. 상태 객체를 사용하면 각 상태가 자체 규칙을 담은 하나의 클래스가 됩니다:
# 패턴 없이
def can_go?
case @status
when :red then false
when :green then true
when :yellow then false
end
end
# 패턴과 함께
def can_go?
@state.can_go?
end상태는 적합하지 않은 이벤트를 거부할 수 있습니다. 여기서는 초안이 바로 게시될 수 없으며, 먼저 검토를 거쳐야 합니다:
class Draft
def submit
Review.new
end
def publish
raise ArgumentError, "cannot publish a draft"
end
def to_s
"draft"
end
end
class Review
def publish
Published.new
end
def to_s
"in review"
end
end
class Published
def to_s
"published"
end
end
state = Draft.new
begin
state.publish
rescue ArgumentError => e
puts e.message
end
state = state.submit
state = state.publish
puts state출력:
cannot publish a draft
published공유 기본 클래스는 모든 상태에 기본 응답을 제공할 수 있습니다. 예를 들어 모든 이벤트를 거부하도록 하여 각 상태가 수락하는 이벤트만 정의하도록 할 수 있습니다:
class State
def submit
raise ArgumentError, "cannot submit #{self}"
end
def approve
raise ArgumentError, "cannot approve #{self}"
end
end
class Review < State
def approve
Published.new
end
end챌린지
쉬움신호등을 완성하세요. Red, Green, Yellow 상태에는 각각 next_state(red에서 green으로, green에서 yellow로, yellow에서 red로 이동), can_go?(green일 때만 가능), to_s(색상)가 있습니다. TrafficLight는 red에서 시작하고, change는 다음 상태로 이동하며, status는 상태에 물어 red: stop 또는 green: go를 반환합니다.
제공된 코드는 change 또는 status 명령을 읽고 각 명령 후 상태를 출력합니다.
코드는 red.rb, green.rb, yellow.rb, traffic_light.rb에 작성하세요. main.rb에는 제공된 입력/출력 코드가 들어 있으며 편집할 수 없습니다.
직접 해보기
require_relative 'red'
require_relative 'green'
require_relative 'yellow'
require_relative 'traffic_light'
# 제공된 입력/출력 코드: 그대로 유지하세요
input = []
while (line = gets)
input << line.chomp
end
light = TrafficLight.new
input.each do |cmd|
light.change if cmd == "change"
puts light.status
end
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 Ruby 컴파일러