Observer (Наблюдатель)

Паттерн проектирования Observer
Описание Observer
Наблюдатель (англ. Observer) — поведенческий шаблон проектирования. Также известен как «подчинённые» (Dependents).
Создает механизм у класса, который позволяет получать экземпляру объекта этого класса оповещения от других объектов об изменении их состояния, тем самым наблюдая за ними.
Шаблон «наблюдатель» применяется в тех случаях, когда система обладает следующими свойствами:
- существует, как минимум, один объект, рассылающий сообщения
- имеется не менее одного получателя сообщений, причём их количество и состав могут изменяться во время работы приложения
- нет надобности очень сильно связывать взаимодействующие объекты, что полезно для повторного использования.
Данный шаблон часто применяют в ситуациях, в которых отправителя сообщений не интересует, что делают получатели с предоставленной им информацией.
Примеры реализации
// Observer Pattern in JavaScript
class Subject {
constructor() {
this.observers = [];
}
attach(observer) {
this.observers.push(observer);
}
detach(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} received: ${data}`);
}
}
// Usage
const subject = new Subject();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');
subject.attach(observer1);
subject.attach(observer2);
subject.notify('Hello World!');
// Observer Pattern in C++
#include <iostream>
#include <vector>
#include <memory>
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& data) = 0;
};
class Subject {
private:
std::vector<std::shared_ptr<Observer>> observers;
public:
void attach(std::shared_ptr<Observer> observer) {
observers.push_back(observer);
}
void detach(std::shared_ptr<Observer> observer) {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notify(const std::string& data) {
for (auto& observer : observers) {
observer->update(data);
}
}
};
class ConcreteObserver : public Observer {
private:
std::string name;
public:
ConcreteObserver(const std::string& name) : name(name) {}
void update(const std::string& data) override {
std::cout << name << " received: " << data << std::endl;
}
};
// Observer Pattern in Go
package main
import "fmt"
type Observer interface {
Update(data string)
}
type Subject struct {
observers []Observer
}
func (s *Subject) Attach(observer Observer) {
s.observers = append(s.observers, observer)
}
func (s *Subject) Detach(observer Observer) {
for i, obs := range s.observers {
if obs == observer {
s.observers = append(s.observers[:i], s.observers[i+1:]...)
break
}
}
}
func (s *Subject) Notify(data string) {
for _, observer := range s.observers {
observer.Update(data)
}
}
type ConcreteObserver struct {
name string
}
func (o *ConcreteObserver) Update(data string) {
fmt.Printf("%s received: %s\n", o.name, data)
}
// Usage
func main() {
subject := &Subject{}
observer1 := &ConcreteObserver{"Observer 1"}
observer2 := &ConcreteObserver{"Observer 2"}
subject.Attach(observer1)
subject.Attach(observer2)
subject.Notify("Hello World!")
}
# Observer Pattern in Python
from abc import ABC, abstractmethod
class Observer(ABC):
@abstractmethod
def update(self, data):
pass
class Subject:
def __init__(self):
self.observers = []
def attach(self, observer):
self.observers.append(observer)
def detach(self, observer):
self.observers.remove(observer)
def notify(self, data):
for observer in self.observers:
observer.update(data)
class ConcreteObserver(Observer):
def __init__(self, name):
self.name = name
def update(self, data):
print(f"{self.name} received: {data}")
# Usage
if __name__ == "__main__":
subject = Subject()
observer1 = ConcreteObserver("Observer 1")
observer2 = ConcreteObserver("Observer 2")
subject.attach(observer1)
subject.attach(observer2)
subject.notify("Hello World!")
<?php
// Observer Pattern in PHP
interface Observer {
public function update($data);
}
class Subject {
private $observers = [];
public function attach(Observer $observer) {
$this->observers[] = $observer;
}
public function detach(Observer $observer) {
$key = array_search($observer, $this->observers);
if ($key !== false) {
unset($this->observers[$key]);
}
}
public function notify($data) {
foreach ($this->observers as $observer) {
$observer->update($data);
}
}
}
class ConcreteObserver implements Observer {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function update($data) {
echo $this->name . " received: " . $data . "\n";
}
}
// Usage
$subject = new Subject();
$observer1 = new ConcreteObserver("Observer 1");
$observer2 = new ConcreteObserver("Observer 2");
$subject->attach($observer1);
$subject->attach($observer2);
$subject->notify("Hello World!");
?>
Паттерн описан Андреем Болониным.