Hura! Mamy wreszcie przyjemność udostępnić wam polską wersję! Zapraszamy do przesyłania wiadomości z waszymi uwagami i informacjami o zauważonych błędach.
Obserwator

Obserwator w języku Ruby

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.

Użycie wzorca w języku Ruby

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: 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

Obserwator w innych językach

Wzorce projektowe: Obserwator w języku Java Wzorce projektowe: Obserwator w języku C# Wzorce projektowe: Obserwator w języku C++ Wzorce projektowe: Obserwator w języku PHP Wzorce projektowe: Obserwator w języku Python Wzorce projektowe: Obserwator w języku Swift Wzorce projektowe: Obserwator w języku TypeScript Wzorce projektowe: Obserwator w języku Go