Letture

 

Observer design pattern con Ruby

Pubblicato translation missing: it.datetime.distance_in_words.almost_x_years fa da Andrea

Observer è uno dei design pattern che mi capita di usare più spesso scrivendo applicazioni con Ruby on Rails: quando si ha a che fare con interazioni tra modelli la cosa più sensata da fare è infatti ricorrere ad un observer di ActiveRecord.

Observer è un design pattern tanto frequente e utile che persino la Standard Library di Ruby include un modulo (chiamato non a caso Observable) che ci semplifica la vita ogni qual volta abbiamo bisogno di una classe che al verificarsi di determinate condizioni si occupi di informare altri oggetti.

Ho realizzato un semplice esempio che sfrutta il modulo Observable per realizzare un gioco simile al bingo; si inizia creando la classe che si occuperà di gestire il gioco informando i giocatori sui numeri estratti:

require 'observer'

class Bingo
  include Observable
  
  NUMBERS = (1..90).to_a
  
  def initialize
    @remaining_numbers = NUMBERS.sort_by {rand}
    @players = []
  end
  
  def start
    extract until @remaining_numbers.empty?
  end
  
  def extract
    extracted = @remaining_numbers.pop
    changed
    notify_observers(extracted)
  end
end

I passi da seguire qui sono:

  • caricare e includere il modulo Observable nella classe
  • creare un array (in questo caso @players) che contenga gli oggetti da tenere informati
  • ogni qual volta si vuole inviare una notifica si deve usare il metodo changed per dichiarare che c’è stato un cambiamento e successivamente chiamare il metodo notify_observers

Procediamo ora con la creazione della classe Player che si occuperà di ricevere le notifiche in arrivo e agire di conseguenza:

class Player
  def initialize(name, numbers)
    @name = name
    @numbers = numbers
  end
  
  def update(extracted)
    @numbers.delete(extracted)
    call_for_victory if @numbers.empty?
  end

  def call_for_victory
    puts "Bingo! #{@name} won this game."
    exit
  end
end

Qui l’unico passo richiesto è creare il metodo update che verrà chiamato ad ogni notifica.
A questo punto non ci resta che creare qualche instanza delle classi e iniziare il gioco, che si concluderà con la vittoria del primo giocatore che avrà visto chiamare tutti i suoi numeri:

game = Bingo.new
fred = Player.new('Fred', [39, 65, 66, 19, 5, 51])
wilma = Player.new('Wilma', [8, 16, 66, 50, 90, 12])
barney = Player.new('Barney', [3, 10, 40, 22, 67, 34])

[fred, wilma, barney].each do |player|
  game.add_observer player
end

game.start

Archiviato in Ruby, Design Patterns