Obserwator to behawioralny wzorzec projektowy pozwalający obiektom powiadamiać inne obiekty o zmianach swojego stanu.
Obserwator daje możliwość subskrypcji lub zrezygnowania z subskrypcji zdarzeń dowolnego obiektu implementującego interfejs subskrybenta.
Złożoność:
Popularność:
Przykłady użycia: Wzorzec Obserwator jest dość powszechny w kodzie Ruby, szczególnie w komponentach graficznego interfejsu użytkownika. Pozwala reagować na zdarzenia dotyczące innych obiektów bez konieczności sprzęgania z klasami tych obiektów.
Identyfikacja: Wzorzec Obserwator można poznać po obecności metod służących subskrypcji, które przechowują obiekty w strukturze listy i po wywołaniach metod aktualizacji obiektów z tej listy.
Przykład koncepcyjny
Poniższy przykład ilustruje strukturę wzorca Obserwator ze szczególnym naciskiem na następujące kwestie:
Z jakich składa się klas?
Jakie role pełnią te klasy?
W jaki sposób elementy wzorca są ze sobą powiązane?
main.rb: Przykład koncepcyjny
# 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: Wynik działania
Subject: Attached an observer.
Subject: Attached an observer.
Subject: I'm doing something important.
Subject: My state has just changed to: 1
Subject: Notifying observers...
ConcreteObserverA: 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
Obserwator w innych językach