Что такое паттерн Observer (Наблюдатель)?

Ответ

Паттерн Observer (Наблюдатель) — это поведенческий паттерн проектирования, который определяет отношение «один ко многим» между объектами. При изменении состояния одного объекта (Subject) все зависимые от него объекты (Observers) автоматически уведомляются и обновляются.

Зачем он нужен? Для реализации механизма подписки, позволяющего одним объектам следить за изменениями в других объектах, не создавая при этом жёсткой связи между ними.

Ключевые компоненты:

  • Subject (Издатель): Хранит список наблюдателей, предоставляет методы для подписки/отписки и уведомляет их об изменениях.
  • Observer (Наблюдатель): Интерфейс с методом update(), который вызывается при изменении состояния издателя.
  • ConcreteSubject: Конкретный издатель, хранящий состояние, интересующее наблюдателей.
  • ConcreteObserver: Конкретный наблюдатель, который реагирует на уведомления от издателя.

Пример на Java:

import java.util.ArrayList;
import java.util.List;

// 1. Интерфейс наблюдателя
interface Observer {
    void update(String message);
}

// 2. Интерфейс субъекта (издателя)
interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
}

// 3. Конкретный издатель (например, система новостей)
class NewsAgency implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String latestNews;

    public void setNews(String news) {
        this.latestNews = news;
        notifyObservers(); // Автоматическое уведомление при изменении
    }

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(latestNews);
        }
    }
}

// 4. Конкретный наблюдатель (например, новостной канал)
class NewsChannel implements Observer {
    private String name;
    private String receivedNews;

    public NewsChannel(String name) {
        this.name = name;
    }

    @Override
    public void update(String news) {
        this.receivedNews = news;
        display();
    }

    public void display() {
        System.out.println(name + " получил новость: " + receivedNews);
    }
}

// 5. Клиентский код
public class Main {
    public static void main(String[] args) {
        NewsAgency agency = new NewsAgency(); // ConcreteSubject

        Observer channel1 = new NewsChannel("Канал 1"); // ConcreteObserver
        Observer channel2 = new NewsChannel("Канал 2"); // ConcreteObserver

        agency.attach(channel1);
        agency.attach(channel2);

        // При изменении состояния издателя все наблюдатели уведомляются
        agency.setNews("Вышла новая версия Java!");
        // Вывод:
        // Канал 1 получил новость: Вышла новая версия Java!
        // Канал 2 получил новость: Вышла новая версия Java!

        agency.detach(channel2);
        agency.setNews("Важное обновление безопасности!");
        // Вывод: Канал 1 получил новость: Важное обновление безопасности!
    }
}

Преимущества:

  • Слабая связность: Издатель не знает деталей реализации наблюдателей.
  • Динамические отношения: Наблюдатели могут подписываться и отписываться во время выполнения.
  • Широкое применение: Лежит в основе реактивных и событийно-ориентированных архитектур.

Недостатки:

  • Наблюдатели могут получать уведомления в случайном порядке.
  • Неуправляемое уведомление всех наблюдателей может привести к проблемам с производительностью, если их очень много.

Ответ 18+ 🔞

А, паттерн Observer, ёпта! Это ж классика, блядь, как "Муму" Тургенева, только без утоплений, надеюсь. Сейчас разжуём, что за зверь.

Представь, есть у тебя один важный чувак — Издатель (Subject). Он, сука, центр вселенной в своей маленькой схеме. А вокруг него крутится овердохуища Наблюдателей (Observers), которые ловят каждую его новую мысль. Он чихнул — они уже "будь здоров!" кричат. Он изменил своё внутреннее состояние — они все, как по команде, должны обновиться.

Зачем эта петрушка? Чтобы не бегать за каждым наблюдателем вручную, не орать "Эй, ты, пизда с ушами, я тут состояние поменял!". Издатель просто живёт своей жизнью, а все, кто на него подписался, получают оповещение на халяву. Слабая связность, блядь, мечта! Как в хорошем паблике: подписался — получай уведомления, надоело — отписался и иди нахуй.

Из чего этот конструктор лего состоит:

  1. Subject (Издатель). Это царь и бог. У него есть список всех подписчиков-лоботрясов. Он умеет их добавлять (attach), выгонять пинком под жопу (detach) и орать на всех сразу "Чё встали, обновились, блядь!" (notifyObservers).
  2. Observer (Наблюдатель). Это интерфейс, контракт для всех, кто хочет слушать царя. Всего один метод — update(). Типа, "я готов, шли уже данные, ебать!"
  3. ConcreteSubject (Конкретный царь). Ну, например, Новостное агентство. У него есть главное — состояние (latestNews). Как только новость обновили, оно сразу орёт всем подписчикам.
  4. ConcreteObserver (Конкретный лоботряс). Допустим, Телеканал. Получил уведомление — обновил свою ленту и побежал показывать.

Смотри, как это в коде выглядит, сука (код не трогаем, он святой):

// 1. Интерфейс лоботряса (все должны уметь одно)
interface Observer {
    void update(String message); // "Эй, чмошник, вот тебе новость!"
}

// 2. Интерфейс царя (все цари должны уметь командовать)
interface Subject {
    void attach(Observer observer); // "Встань в строй!"
    void detach(Observer observer); // "Свободен, пошёл нахуй!"
    void notifyObservers(); // "Внимание, всем обновиться, блядь!"
}

// 3. Конкретный царь - Новостное агентство
class NewsAgency implements Subject {
    private List<Observer> observers = new ArrayList<>(); // Список подписчиков
    private String latestNews; // Его священное состояние

    // Вот тут магия! Выпустили новость — и сразу всем сигнал!
    public void setNews(String news) {
        this.latestNews = news;
        notifyObservers(); // Автоматический пинок всем наблюдателям
    }

    @Override
    public void attach(Observer observer) {
        observers.add(observer); // Подписался, молодец
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer); // Отписался, иди в пизду
    }

    @Override
    public void notifyObservers() {
        // Проходим по всем и каждого пинаем
        for (Observer observer : observers) {
            observer.update(latestNews);
        }
    }
}

// 4. Конкретный лоботряс - Новостной канал
class NewsChannel implements Observer {
    private String name;
    private String receivedNews;

    public NewsChannel(String name) {
        this.name = name;
    }

    // Вот его реакция на пинок от издателя
    @Override
    public void update(String news) {
        this.receivedNews = news; // Получил новость
        display(); // И сразу её вывел
    }

    public void display() {
        System.out.println(name + " получил новость: " + receivedNews);
    }
}

// 5. Клиентский код, где всё оживает
public class Main {
    public static void main(String[] args) {
        NewsAgency agency = new NewsAgency(); // Создали царя

        Observer channel1 = new NewsChannel("Первый блядский"); // Создали лоботрясов
        Observer channel2 = new NewsChannel("Второй сопливый");

        agency.attach(channel1); // Подписали первого
        agency.attach(channel2); // Подписали второго

        // Царь меняет состояние — все лоботрясы дёргаются!
        agency.setNews("Вышла Java 25! Теперь код пишется силой мысли!");
        // Вывод:
        // Первый блядский получил новость: Вышла Java 25!...
        // Второй сопливый получил новость: Вышла Java 25!...

        agency.detach(channel2); // Второй канал отписался, пошёл нахуй
        agency.setNews("Обнаружена уязвимость: все паттерны — хуйня!");
        // Вывод: Только Первый блядский получил новость...
    }
}

Плюсы, блядь:

  • Связь слабая, как у алкоголика с реальностью. Издателю похуй, кто и как обрабатывает его новости.
  • Подписки динамические. Хочешь — вставай в строй, не хочешь — вали. Во время работы программы!
  • Применение — везде. Всякие GUI, event-driven архитектуры, реактивщина — тут этот паттерн пальцем делает.

Минусы, ёпта:

  • Порядок уведомлений — хуй пойми. Кто первый встал, того и тапки. Может, это важно.
  • Если наблюдателей дохуя, а издатель орет на всех без разбора — можно получить лавину обновлений и производительность ебать. Надо с умом.