Menu
Coddy logo textTech

Patrón State

Parte de la sección Programación orientada a objetos del Journey de Ruby de Coddy. Lección 49 de 57.

El patrón de estado traslada el comportamiento que depende del estado actual de un objeto a objetos de estado independientes. El objeto principal, el contexto, contiene un estado y reenvía las llamadas a este; un estado responde y decide cuál será el siguiente estado:

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

Salida:

light is red
light is green
light is red

Sin el patrón, cada método del contexto necesita un case sobre un valor de estado, y un estado nuevo implica editar todos ellos. Con objetos de estado, cada estado es una clase que contiene sus propias reglas:

# sin el patrón
def can_go?
  case @status
  when :red then false
  when :green then true
  when :yellow then false
  end
end

# con el patrón
def can_go?
  @state.can_go?
end

Los estados pueden rechazar un evento que no corresponda. Aquí, un borrador no se puede publicar directamente; primero tiene que pasar por revisión:

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

Salida:

cannot publish a draft
published

Una clase base compartida puede proporcionar la respuesta predeterminada a cada estado, como rechazar todos los eventos, de modo que cada estado solo defina los eventos que acepta:

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

Desafío

Fácil

Completa el semáforo. Los estados Red, Green y Yellow tienen cada uno next_state (red pasa a green, green a yellow, yellow a red), can_go? (solo green) y to_s (el color). TrafficLight comienza en red, change pasa al siguiente estado y status devuelve red: stop o green: go, preguntándole al estado.

El código proporcionado lee los comandos change o status e imprime el estado después de cada comando.

Tu código debe ir en red.rb, green.rb, yellow.rb y traffic_light.rb. main.rb contiene el código proporcionado de entrada/salida y no se puede editar.

Pruébalo tú mismo

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

# Código de entrada/salida suministrado: déjalo tal 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 iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Compilador de Ruby online