← Назад к вопросам

Как работает 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 приложениях.