状态模式
本课属于 Coddy Ruby 学习旅程的“面向对象编程”单元。第 49 课,共 57 课。
状态模式会将依赖对象当前状态的行为移到单独的状态对象中。主对象上下文持有一个状态,并将调用转发给它;状态会响应调用,并决定下一个状态是什么:
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(红灯变绿灯,绿灯变黄灯,黄灯变红灯)、can_go?(只有绿灯可以通行)和 to_s(颜色)。TrafficLight 从红灯开始,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 在线编译器