Menu
Coddy logo textTech

Padrão State

Parte da seção Programação orientada a objetos do Journey de Ruby da Coddy. Lição 49 de 57.

O padrão de estado move o comportamento que depende do estado atual de um objeto para objetos de estado separados. O objeto principal, o contexto, mantém um estado e encaminha as chamadas para ele; um estado responde e decide qual estado vem a seguir:

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

Saída:

light is red
light is green
light is red

Sem o padrão, cada método do contexto precisa de um case sobre um valor de status, e um novo estado significa editar todos eles. Com objetos de estado, cada estado é uma classe que contém suas próprias regras:

# sem o padrão
def can_go?
  case @status
  when :red then false
  when :green then true
  when :yellow then false
  end
end

# com o padrão
def can_go?
  @state.can_go?
end

Os estados podem recusar um evento que não seja adequado. Aqui, um rascunho não pode ser publicado diretamente; primeiro, ele precisa passar por revisão:

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

Saída:

cannot publish a draft
published

Uma classe base compartilhada pode fornecer a resposta padrão para cada estado, como recusar todos os eventos, para que cada estado defina apenas os eventos que aceita:

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
challenge icon

Desafio

Fácil

Complete o semáforo. Os estados Red, Green e Yellow têm, cada um, next_state (red vai para green, green para yellow, yellow para red), can_go? (apenas green) e to_s (a cor). TrafficLight começa em red, change move para o próximo estado, e status retorna red: stop ou green: go, consultando o estado.

O código fornecido lê os comandos change ou status e imprime o status após cada comando.

Seu código deve estar em red.rb, green.rb, yellow.rb e traffic_light.rb. main.rb contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

require_relative 'red'
require_relative 'green'
require_relative 'yellow'
require_relative 'traffic_light'

# Código de entrada/saída fornecido: mantenha-o como está
input = []
while (line = gets)
  input << line.chomp
end
light = TrafficLight.new
input.each do |cmd|
  light.change if cmd == "change"
  puts light.status
end
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação orientada a objetos

Pratique por conta própria: Compilador de Ruby online