
Observateur en Ruby
L’Observateur est un patron de conception comportemental qui permet à certains objets d’envoyer des notifications concernant leur état à d’autres objets.
Ce patron fournit la possibilité aux objets qui implémentent une interface de souscription, de s’inscrire et de se désinscrire de ces événements.
Utilisation du patron de conception en Ruby
Complexité :
Popularité :
Exemples d’utilisation : L’observateur est assez répandu en Ruby, surtout dans les composants GUI. Il fournit une manière de réagir aux événements qui se produisent chez d’autres objets sans se coupler à leurs classes.
Identification : Ce patron peut être reconnu dans les méthodes de souscription qui stockent des objets dans une liste et par les appels des objets de cette liste à la méthode update.
Exemple conceptuel
Dans cet exemple, nous allons voir la structure de l’Observateur. Nous allons répondre aux questions suivantes :
- Que contiennent les classes ?
- Quels rôles jouent-elles ?
- Comment les éléments du patron sont-ils reliés ?
main.rb: Exemple conceptuel
# The Subject interface declares a set of methods for managing subscribers.
class Subject
# Attach an observer to the subject.
def attach(observer)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
# Detach an observer from the subject.
def detach(observer)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
# Notify all observers about an event.
def notify
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# The Subject owns some important state and notifies observers when the state
# changes.
class ConcreteSubject < Subject
# For the sake of simplicity, the Subject's state, essential to all
# subscribers, is stored in this variable.
attr_accessor :state
# @!attribute observers
# @return [Array<Observer>] attr_accessor :observers private :observers
def initialize
@observers = []
end
# List of subscribers. In real life, the list of subscribers can be stored
# more comprehensively (categorized by event type, etc.).
# @param [Obserser] observer
def attach(observer)
puts 'Subject: Attached an observer.'
@observers << observer
end
# @param [Obserser] observer
def detach(observer)
@observers.delete(observer)
end
# The subscription management methods.
# Trigger an update in each subscriber.
def notify
puts 'Subject: Notifying observers...'
@observers.each { |observer| observer.update(self) }
end
# Usually, the subscription logic is only a fraction of what a Subject can
# really do. Subjects commonly hold some important business logic, that
# triggers a notification method whenever something important is about to
# happen (or after it).
def some_business_logic
puts "\nSubject: I'm doing something important."
@state = rand(0..10)
puts "Subject: My state has just changed to: #{@state}"
notify
end
end
# The Observer interface declares the update method, used by subjects.
class Observer
# Receive update from subject.
def update(_subject)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# Concrete Observers react to the updates issued by the Subject they had been
# attached to.
class ConcreteObserverA < Observer
# @param [Subject] subject
def update(subject)
puts 'ConcreteObserverA: Reacted to the event' if subject.state < 3
end
end
class ConcreteObserverB < Observer
# @param [Subject] subject
def update(subject)
return unless subject.state.zero? || subject.state >= 2
puts 'ConcreteObserverB: Reacted to the event'
end
end
# The client code.
subject = ConcreteSubject.new
observer_a = ConcreteObserverA.new
subject.attach(observer_a)
observer_b = ConcreteObserverB.new
subject.attach(observer_b)
subject.some_business_logic
subject.some_business_logic
subject.detach(observer_a)
subject.some_business_logic
output.txt: Résultat de l’exécution
Subject: Attached an observer.
Subject: Attached an observer.
Subject: I'm doing something important.
Subject: My state has just changed to: 2
Subject: Notifying observers...
ConcreteObserverA: Reacted to the event
ConcreteObserverB: Reacted to the event
Subject: I'm doing something important.
Subject: My state has just changed to: 10
Subject: Notifying observers...
ConcreteObserverB: Reacted to the event
Subject: I'm doing something important.
Subject: My state has just changed to: 2
Subject: Notifying observers...
ConcreteObserverB: Reacted to the event