← Назад к вопросам
Как работает Observer?
2.0 Middle🔥 161 комментариев
#SOLID и паттерны проектирования#ООП
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Observer Pattern: Полное руководство
Observer (Наблюдатель) — это поведенческий паттерн проектирования, который устанавливает отношение один-ко-многим между объектами так, что при изменении состояния одного объекта все зависящие от него объекты уведомляются об этом автоматически.
Основная концепция
Представьте газету (Subject) и читателей (Observers):
- Газета выпускает новое издание
- Все подписчики уведомляются и получают копию
- Подписчики не знают друг о друге
- Газета не знает, как читатели используют газету
Структура паттерна
Subject (издатель):
- Хранит список Observer'ов
- Уведомляет наблюдателей об изменениях
Observer (наблюдатель):
- Получает уведомления от Subject'а
- Реагирует на изменения
Практическая реализация
Интерфейсы
// Observer - интерфейс для подписчиков
public interface Observer {
void update(String message);
}
// Subject - издатель
public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
Конкретная реализация Subject'а
public class NewsAgency implements Subject {
private String news;
private List<Observer> observers = new ArrayList<>();
// Подписка
@Override
public void attach(Observer observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
// Отписка
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
// Уведомление всех наблюдателей
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(news);
}
}
// Публикация новой информации
public void publishNews(String news) {
this.news = news;
notifyObservers(); // Автоматически уведомляем всех
}
}
Конкретные наблюдатели
// Читатель газеты
public class Reader implements Observer {
private String name;
public Reader(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " получил новость: " + message);
}
}
// Телевизионная станция
public class TvChannel implements Observer {
private String channelName;
public TvChannel(String channelName) {
this.channelName = channelName;
}
@Override
public void update(String message) {
System.out.println(channelName + " транслирует: " + message);
}
}
// Социальная сеть
public class SocialNetwork implements Observer {
private String platform;
public SocialNetwork(String platform) {
this.platform = platform;
}
@Override
public void update(String message) {
System.out.println(platform + " публикует: " + message);
}
}
Использование
public class Main {
public static void main(String[] args) {
// Создаем издателя
NewsAgency agency = new NewsAgency();
// Создаем наблюдателей (подписчиков)
Reader reader1 = new Reader("Иван");
Reader reader2 = new Reader("Мария");
TvChannel tvChannel = new TvChannel("Первый канал");
SocialNetwork twitter = new SocialNetwork("Twitter");
// Подписываем наблюдателей
agency.attach(reader1);
agency.attach(reader2);
agency.attach(tvChannel);
agency.attach(twitter);
// Публикуем новость - все автоматически уведомляются
agency.publishNews("Новое открытие в IT технологиях!");
// Вывод:
// Иван получил новость: Новое открытие в IT технологиях!
// Мария получил новость: Новое открытие в IT технологиях!
// Первый канал транслирует: Новое открытие в IT технологиях!
// Twitter публикует: Новое открытие в IT технологиях!
// Отписываем одного наблюдателя
agency.detach(reader2);
// Публикуем еще одну новость
agency.publishNews("Запуск нового спутника!");
// Вывод:
// Иван получил новость: Запуск нового спутника!
// Первый канал транслирует: Запуск нового спутника!
// Twitter публикует: Запуск нового спутника!
}
}
Observer в Java: встроенные решения
1. java.util.Observable и java.util.Observer (deprecated)
// Старый подход (не рекомендуется)
public class NewsAgencyOld extends Observable {
private String news;
public void publishNews(String news) {
this.news = news;
setChanged(); // Отметить изменение
notifyObservers(news); // Уведомить всех
}
}
public class ReaderOld implements Observer {
private String name;
public ReaderOld(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name + ": " + arg);
}
}
Этот подход deprecated с Java 9.
2. RxJava / Reactive Extensions
// Современный подход с RxJava
Subject<String> newsSubject = PublishSubject.create();
// Подписчики
newsSubject.subscribe(news -> System.out.println("Читатель 1: " + news));
newsSubject.subscribe(news -> System.out.println("Читатель 2: " + news));
// Публикация
newsSubject.onNext("Важная новость!");
3. Spring Events (Event-driven architecture)
// Издатель
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// Сохраняем заказ
Order savedOrder = orderRepository.save(order);
// Публикуем событие
publisher.publishEvent(new OrderCreatedEvent(savedOrder));
}
}
// Событие
public class OrderCreatedEvent extends ApplicationEvent {
private Order order;
public OrderCreatedEvent(Order order) {
super(order);
this.order = order;
}
public Order getOrder() {
return order;
}
}
// Слушатель 1
@Component
public class OrderNotificationListener {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
Order order = event.getOrder();
System.out.println("Отправляем уведомление: " + order.getId());
}
}
// Слушатель 2
@Component
public class OrderAnalyticsListener {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
Order order = event.getOrder();
System.out.println("Логируем аналитику: " + order.getTotalAmount());
}
}
Advantages & Disadvantages
Преимущества:
- Слабая связанность — Subject и Observer слабо связаны
- Dynamic subscriptions — можно подписываться/отписываться runtime
- Broadcasting communication — один Subject может уведомить много Observer'ов
- Separation of concerns — издатель не знает о подписчиках
Недостатки:
- Сложность отладки — трудно отследить поток выполнения
- Memory leaks — если забыть отписаться, объект не будет удален из памяти
- Order unpredictability — порядок уведомления непредсказуем
- Performance — если много наблюдателей, может быть медленно
Real-world примеры в Java
// 1. Event Listeners в UI (Swing/JavaFX)
button.addActionListener(e -> System.out.println("Кнопка нажата"));
// 2. PropertyChangeListener
property.addPropertyChangeListener(evt -> {
System.out.println("Старое значение: " + evt.getOldValue());
System.out.println("Новое значение: " + evt.getNewValue());
});
// 3. DatabaseListener для изменения данных
listener.onChange(user -> System.out.println("Пользователь обновлен: " + user));
// 4. WebSocket для real-time updates
webSocketSession.subscribe(message -> {
System.out.println("Получено: " + message);
});
Best Practices
1. Избегайте утечек памяти:
// Всегда отписывайтесь
public void cleanup() {
subject.detach(observer);
}
// В Spring используйте @EventListener (автоматическая очистка)
2. Используйте типобезопасность:
// Вместо Object используйте конкретные типы
@FunctionalInterface
public interface Observer<T> {
void update(T data);
}
3. Обрабатывайте исключения:
@Override
public void notifyObservers() {
for (Observer observer : observers) {
try {
observer.update(news);
} catch (Exception e) {
logger.error("Observer failed", e);
}
}
}
4. Рассмотрите асинхронность:
// Используйте async для неблокирующих уведомлений
@EventListener
@Async
public void onOrderCreated(OrderCreatedEvent event) {
// Обработка в отдельном потоке
}
Observer паттерн — основа event-driven архитектуры в современных Java приложениях.