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

Что такое паттерн Observer?

2.0 Middle🔥 181 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Observer (Наблюдатель): Паттерн поведенческого проектирования

Observer — это поведенческий паттерн проектирования, который создает механизм подписки для объектов, позволяющий одним объектам (подписчикам или наблюдателям) следить и реагировать на изменения в другом объекте (издателе или субъекте).

Основная концепция

Паттерн определяет отношение «один-ко-многим» между объектами. Когда состояние издателя (субъекта) изменяется, все его подписчики (наблюдатели) автоматически уведомляются и обновляются. Это позволяет достичь слабой связанности между компонентами системы: издатель не знает деталей реализации подписчиков, а лишь уведомляет их через общий интерфейс.

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

  1. Subject (Субъект/Издатель):
    *   Объект, за которым ведется наблюдение.
    *   Содержит список подписчиков и методы для управления ими: `attach`, `detach`.
    *   Содержит метод `notify`, который инициирует рассылку уведомлений всем подписчикам при изменении своего состояния.

  1. Observer (Наблюдатель/Подписчик):
    *   Интерфейс или протокол, который определяет метод обновления (например, `update`). Все конкретные наблюдатели реализуют этот интерфейс.

  1. ConcreteSubject (Конкретный субъект):
    *   Конкретная реализация субъекта. Хранит состояние, интересующее наблюдателей, и отправляет уведомления при его изменении.

  1. ConcreteObserver (Конкретный наблюдатель):
    *   Конкретная реализация наблюдателя. Реагирует на уведомления от субъекта, запрашивая у него новое состояние и выполняя свою логику в ответ на изменения.

Реализация в Swift

Рассмотрим классическую реализацию на примере системы новостной рассылки.

// 1. Протокол Наблюдателя (Observer)
protocol Observer: AnyObject {
    func update(news: String)
}

// 2. Субъект/Издатель (Subject)
class NewsPublisher {
    private var observers = NSHashTable<AnyObject>.weakObjects() // Коллекция со слабыми ссылками
    private var latestNews: String = "" {
        didSet {
            notifyObservers()
        }
    }
    
    // Методы управления подпиской
    func attach(observer: Observer) {
        observers.add(observer)
    }
    
    func detach(observer: Observer) {
        observers.remove(observer)
    }
    
    // Приватный метод уведомления
    private func notifyObservers() {
        observers.allObjects.compactMap { $0 as? Observer }.forEach { observer in
            observer.update(news: latestNews)
        }
    }
    
    // Бизнес-логика, изменяющая состояние
    func publish(news: String) {
        self.latestNews = news
        print("Издатель: Опубликована новость - '\(news)'")
    }
}

// 3. Конкретные Наблюдатели (ConcreteObserver)
class EmailSubscriber: Observer {
    private let name: String
    
    init(name: String) {
        self.name = name
    }
    
    func update(news: String) {
        print("Подписчик \(name) получил email: \(news)")
    }
}

class MobileApp: Observer {
    func update(news: String) {
        print("Мобильное приложение отобразило push-уведомление: \(news)")
    }
}

// 4. Использование
let publisher = NewsPublisher()

let alice = EmailSubscriber(name: "Алиса")
let bob = EmailSubscriber(name: "Боб")
let app = MobileApp()

publisher.attach(observer: alice)
publisher.attach(observer: bob)
publisher.attach(observer: app)

publisher.publish(news: "Вышел новый Swift 6!")
// Вывод:
// Издатель: Опубликована новость - 'Вышел новый Swift 6!'
// Подписчик Алиса получил email: Вышел новый Swift 6!
// Подписчик Боб получил email: Вышел новый Swift 6!
// Мобильное приложение отобразило push-уведомление: Вышел новый Swift 6!

publisher.detach(observer: bob)
publisher.publish(news: "Представлен Vision Pro")
// Вывод:
// Издатель: Опубликована новость - 'Представлен Vision Pro'
// Подписчик Алиса получил email: Представлен Vision Pro
// Мобильное приложение отобразило push-уведомление: Представлен Vision Pro

Альтернативные реализации в iOS/macOS

В экосистеме Apple паттерн Observer реализован в нескольких встроенных механизмах:

  • NotificationCenter: Классический пример. NotificationCenter.default выступает в роли централизованного субъекта, а наблюдатели регистрируются на определенные уведомления (Notification.Name).

    // Подписчик
    NotificationCenter.default.addObserver(self, selector: #selector(handleNotification), name: .UIKeyboardDidShow, object: nil)
    
    // Издатель
    NotificationCenter.default.post(name: .customEvent, object: nil)
    
  • Key-Value Observing (KVO): Механизм наблюдения за изменениями свойств объектов, соответствующих протоколу NSObject.

    // Наблюдение за свойством
    observedObject.addObserver(self, forKeyPath: #keyPath(MyClass.myProperty), options: .new, context: nil)
    
    // Обработка изменения
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        // Реакция на изменение
    }
    
  • Combine Framework: Современный реактивный подход. Publisher из фреймворка Combine — это реализация субъекта, а sink или assign создают подписчика.

    // Subject из Combine
    let publisher = PassthroughSubject<String, Never>()
    
    // Подписка
    let cancellable = publisher.sink { news in
        print("Получено через Combine: \(news)")
    }
    
    publisher.send("Новое событие")
    

Преимущества и недостатки

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

  • Слабая связанность: Издатель и подписчики не зависят друг от друга конкретными классами.
  • Динамическая подписка: Подписчики могут добавляться и удаляться во время выполнения программы.
  • Принцип открытости/закрытости: Можно вводить новые классы подписчиков, не изменяя код издателя.
  • Упрощает реактивную архитектуру: Идеально подходит для событийно-ориентированных систем.

Недостатки:

  • Непредсказуемый порядок уведомлений: Наблюдатели могут получать уведомления в произвольном порядке.
  • Риск утечек памяти: При неправильной реализации (например, сильные ссылки в коллекции наблюдателей) или забытом отписывании.
  • Сложность отладки: Цепная реакция уведомлений может быть сложной для трассировки.

Применение в iOS-разработке

Паттерн Observer фундаментален и повсеместно используется:

  • Обновление UI при изменении данных модели (MVC, MVVM).
  • Реакция на системные события (появление клавиатуры, изменение ориентации).
  • Передача сообщений между модулями приложения (межмодульная коммуникация).
  • Реализация кастомных делегатов как частный случай паттерна (один наблюдатель).
  • Современные реактивные архитектуры (Combine, RxSwift), где он является базовым принципом.

Вывод: Паттерн Observer — это краеугольный камень гибкой, событийно-управляемой архитектуры. Понимание его принципов критически важно для iOS-разработчика, так как он лежит в основе многих системных механизмов Apple и является предшественником современных реактивных подходов, доминирующих в современной разработке.

Что такое паттерн Observer? | PrepBro