Введение

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

В проектировании и разработке программного обеспечения шаблон наблюдатель (Observer) – это шаблон проектирования, в котором объект, называемый субъектом (subject), ведёт список своих зависимостей, называемых наблюдателями (observers), и автоматически уведомляет их о любых изменениях своего состояния, как правило, вызывая один из их методов. Он часто используется для реализации распределённых систем обработки событий в программном обеспечении, управляемом событиями. В таких системах субъект обычно называют "потоком событий" или "источником событий", а наблюдатели – "потребителями событий". Терминология "поток" отсылает к физической схеме, в которой наблюдатели физически разделены и не имеют контроля над событиями, испускаемыми субъектом/источником. Таким образом, этот шаблон подходит для любого процесса, при котором данные поступают из источника, недоступного процессору при запуске, но поступающего, казалось бы, случайным образом (HTTP-запросы, данные GPIO, пользовательский ввод с периферийных устройств и распределённых баз данных и т.д.).

Сильная или слабая отсылка

Модель наблюдателя может приводить к утечкам памяти, известным как проблема "устаревших слушателей", поскольку в базовой реализации она требует как явной регистрации, так и явной отмены регистрации, подобно шаблону Dispose, из-за того, что объект-издатель хранит сильные ссылки на наблюдателей, не давая им быть освобожденными. Этому можно предотвратить, если объект-издатель будет хранить слабые ссылки на наблюдателей.

Соединение и типичные реализации "публиковать-подписаться"

Как правило, шаблон наблюдателя реализуется таким образом, что наблюдаемый объект является частью того объекта, изменения состояния которого наблюдаются (и сообщаются наблюдателям). Этот тип реализации считается тесно связанным, вынуждая как наблюдателей, так и наблюдаемый объект знать друг о друге и иметь доступ к их внутренним компонентам, что может привести к проблемам масштабируемости, производительности, восстановления сообщений и поддержки (также известной как потеря событий или уведомлений), отсутствию гибкости при условном распространении и потенциальным препятствиям для реализации необходимых мер безопасности. В некоторых (не использующих опрос) реализациях шаблона "издатель-подписчик" это решается путем создания выделенного сервера очереди сообщений (и иногда дополнительного обработчика сообщений) в качестве промежуточного звена между наблюдателем и наблюдаемым объектом, тем самым разделяя компоненты. В этих случаях наблюдатели обращаются к серверу очереди сообщений, используя шаблон наблюдателя, подписываясь на определенные сообщения и зная (или не зная, в некоторых случаях) только об ожидаемом сообщении, при этом ничего не зная об отправителе сообщения; отправитель также может ничего не знать о наблюдателях. Другие реализации шаблона "издатель-подписчик", которые достигают аналогичного эффекта уведомления и обмена информацией с заинтересованными сторонами, не используют шаблон наблюдателя. В ранних реализациях многооконных операционных систем, таких как OS/2 и Windows, термины "publish subscribe pattern" и "event driven software development" использовались как синонимы шаблона наблюдателя. Шаблон наблюдателя, как описано в книге "Паттерны проектирования", является очень базовой концепцией и не рассматривает вопросы отмены интереса к изменениям наблюдаемого объекта или специальной логики, которую должен выполнять наблюдаемый объект до или после уведомления наблюдателей. Шаблон также не решает задачи регистрации уведомлений об изменениях или гарантии их получения. Эти вопросы обычно решаются в системах очередей сообщений, в которых шаблон наблюдателя играет лишь незначительную роль. Связанные шаблоны включают "издатель-подписчик", "посредник" и "одиночка".

Не связанные

Модель наблюдателя может использоваться в ситуациях, когда функциональность "издатель-подписчик" недоступна, например, когда статус модели часто обновляется. Частые обновления могут привести к зависанию интерфейса (например, из-за множества вызовов перерисовки); в таких случаях наблюдателям следует использовать таймер. Вместо перегрузки сообщениями об изменениях, наблюдатель будет обеспечивать отображение приблизительного состояния модели через заданные интервалы времени. Этот подход особенно полезен для индикаторов прогресса, где ход выполнения базовой операции часто меняется.

Диаграмма классов и последовательности UML

В этой диаграмме классов UML класс Subject не обновляет состояние зависимых объектов напрямую. Вместо этого Subject обращается к интерфейсу Observer (update) для обновления состояния, что делает Subject независимым от способа обновления состояния зависимых объектов. Классы Observer1 и Observer2 реализуют интерфейс Observer, синхронизируя свое состояние с состоянием субъекта. На диаграмме последовательностей UML показаны взаимодействия во время выполнения: объекты Observer1 и Observer2 вызывают attach(this) у Subject1 для регистрации. Если состояние Subject1 изменяется, Subject1 вызывает notify у себя. notify вызывает update у зарегистрированных объектов Observer1 и Observer2, которые запрашивают измененные данные (getState) у Subject1 для обновления (синхронизации) своего состояния.

Пример

Хотя классы `java.util.Observer` и `java.util.Observable` существуют в библиотеке Java, они были объявлены устаревшими в Java 9 из-за довольно ограниченной реализованной модели. Ниже приведен пример на Java, который принимает ввод с клавиатуры и обрабатывает каждую строку ввода как событие. Когда строка поступает из `System.in`, метод `notifyObservers` вызывается для уведомления всех наблюдателей о наступлении события посредством вызова их методов `update`.