Observer pattern
LINEРеактивный подход все больше находит применение в современной разработке. Создаются все новые и новые библиотеки, фреймворки. Сердцем реактивного подхода является паттерн observer, или наблюдатель.
Рассмотрим паттерн observer на примере героев популярного мультфильма "Симпсоны".
Барт очень любит комиксы. Комиксы продает Джефф Альбертсон. Когда Барт хочет новый комикс, он идет в магазин к Джеффу и спрашивает, появились ли новые комиксы. Джефф помнит число всех комиксов, которые у него есть. Барт тоже знает все комиксы, которые у него есть. Сравнив число своих комиксов и число комиксов Джеффа, Барт может узнать, есть ли новые комиксы.
Чтобы представить эту ситуацию, нам нужно 3 класса - представляющие Барта, Джеффа и комикс.
Барт знает о своей коллекции комиксов, а также знает о продавце комиксов. Он может проверить, есть ли новые комиксы, проверив размер своей коллекции и коллекции Джеффа (hasNewComics) , а также получить те комиксы, которых ему не хватает (updateComicsCollection).

Джефф Альбертсон также имеет свою коллекцию комиксов. Он может отдать последние n комиксов (getLastComics). Время от времени ему привозят новые комиксы, которые он добавляет в свою коллекцию (addComics).

Комикс представляет из себя название и количество страниц.

Такой подход работает, но Барту приходится постоянно спрашивать Джеффа, есть ли новые комиксы? Если новый комикс может появиться в любой момент, то Барту приходится спрашивать опять и опять (срочки 5-10, 15-20). При этом остается возможность, что Барт пропустит самый новый комикс (строка 25).

Можно привести такой пример. У вас есть приложение, которое в отдельном потоке выполняет сложное вычисление. Когда вычисление закончится, этот поток должен установить флаг complete в true. Как другой поток может узнать о выполнении вычисления? Он должен спросить первый поток, равен ли complete true. Если нет, это действие нужно повторить.
Это напоминает то, как маленький ребенок в долгой дороге спрашивает маму "мы приехали? А теперь? А теперь...".
Такой подход тратит ресурсы системы на бесполезные опросы. Но вернемся к Барту.
В некоторый момент Барт устает ходить к Джеффу. В очередной раз, когда он приходит в магазин, он договаривается с продавцом, что тот позвонить ему (Барту), когда у продавца комиксов появится новый комикс. Барту больше не нужно надеяться на удачу, когда он идет в магазин. Если он идет в магазин, он точно знает, что сегодня сможет почитать новый комикс.
Как это реализовать? Во первых, теперь Джефф должен знать о Барте, чтобы сообщить ему о поступлении нового комикса. Барту понадобится новый метод для получения уведомления о новом комиксе (newComics). Все что останется сделать - Джефф должен вызывать метод Барта, когда получает новый комикс. (поле jeffAlbertsonи методы hasNewComics, updateComicsCollection в классе Bart можно удалить)

Теперь, как только Джефф получит новый комикс, он сообщит об этом Барту (вызвав bart.newComics), и тот в свою очередь заберет новый комикс. Барту больше не нужно самому проверять наличие комиксов и тратить время.


Мы подошли очень близко к паттерну наблюдатель. Можно сказать, что Барт "наблюдает" за поступлением новых комиксов, и получает оповещение каждый раз, когда приходит новый комикс.
Но вот друг Барта, Милхауз, узнал о придуманном Барте способе. Теперь он тоже не хочет ходить в магазин комиксов, а получать оповещения о поступлении.
Сильная связь
Все работает как нужно, но есть одно но. Это проблема сильной связности. Мы завязаны на конкретном продавце (Джеффе) и конкретном покупателе (Барте). Если Джефф заболеет и не выйдет на работу, Барт не получит свое оповещение. К тому же, Джефф не может предложить эту услугу кому либо еще, кроме Барта. Классы Джеффа и Барта связаны между собой.
Если два объекта могут взаимодействовать, не обладая практически никакой информацией друг о друге, такие объекты называют слабосвязанными. Это говорит о хорошем дизайне, т.к. изменение одного компонента не нарушает работу другого.
Чтобы сделать наши классы (и любые другие) слабо связанными, выделяют интерфейсы. Мы говорим, что хотим иметь дело не конкретно с Джеффом, а с любым человеком, который может работать продавцом комиксов. Мы знаем, что этот продавец будет оповещать не только Барта, но и многих других. Для хранения продавцу понадобится список всех людей, желающих знать о поступлении нового комикса. Продавец должен уметь добавлять в этот список (register) и удалять из него (unregister). Когда продавцу приходит новый комикс (newComicsHasArrived), он берет свой список и обзванивает (вызывает метод ) каждого человека из этого списка.

С другой стороны нам нужен покупатель. Не обязательно Барт, любой покупатель. Он должен иметь метод, который сообщает о поступлении нового журнала.

В итоге у нас получится следующая картина. У нас есть абстрактный продавец, есть абстрактный покупатель. Продавец может добавить с свой список покупателя, если тот попросит. Также он может удалить из списка. Когда продавцу приходит новый комикс, он оповещает всех покупателей.

Как теперь будет выглядеть класс JeffAlbertson?

Класс Purchaser можно реализовать как анонимный, переопределив метод newComics.

Если не вызвать метод register (или unregister после register), то продавец не будет оповещать о новых комиксах.
Написав покупателя (тот, кто хочет узнать об изменениях) и продавца (тот, кто сообщает об изменениях) мы реализовали паттерн observer.
Observer pattern
Итак, паттерн наблюдатель состоит из двух частей. Первая часть - это компонент, который обладает меняющимся состоянием и содержит (или получает) данные. В нашем случае это класс Seller. Продавец получает данные в виде нового комикса. Обычно такой класс называется Observable (дословно то, за чем можно наблюдать, наблюдаемый) или Subject.
Вторая часть - это сами наблюдатели. В нашем случае это класс Purchaser. Обычно такой класс называется Observer, т.е. тот, кто наблюдает за Observable.
Все что знает Observable о конкретном наблюдателе - то, что тот реализует интерфейс Observer (продавец знает о покупателе только то, что последний способен сделать покупку).
Новый наблюдатель может добавиться в любой момент, и отписаться от событий тоже в любой момент.
Кроме того, наблюдатели и субъекты независимы, т.е. один наблюдатель может подписаться на 2 субъекта и 1 субъект может содержать 2 наблюдателей.
RxJava
Реактивные библиотеки используют этот подход повсюду. Реактивные от слова реакция, реакция на событие. Пожалуй самой известной такой библиотекой является Rx. Она реализована на множестве разных языков и отличается только синтаксисом того языка, для которого используется. Вот как пример выше можно реализовать с помощью RxJava

PublisherSubject - это наш Observable (технически это не совсем так, в RxJava есть и класс Observable и он несколько отличается от PublisherSubject. Но в плане объект, который содержит данные - это Observable). Барт как обычно расширяет Observer. У наблюдателя нужно реализовать несколько методов, но мы остановимся на одном - onNext(). Каждый раз, когда появится новый комикс, будет вызван onNext(). Это аналог метода newComics.
Вместо метода register используется subscribe (просто другое название). Вместо метода Джеффа newComicsHasArrived используется onNext(). Здесь все схоже, но несколько сложнее и рассчитано на большее количество вариантов использования.
RxJava довольно объемная библиотека, но все что там есть акцентировано по observable и subscriber. Там есть много способов создать Observable, преобразовать данные по пути к подписчику, сделать что либо в другом потоке и многое другое.
Заключение
Паттерн Наблюдатель определяет отношение типа «один-ко-многим» между объектами, которые содержат данные и объектами, которые хотят знать об изменении этих данных.
Источником события может быть все что угодно: пользователь ввел новые данные, пришло новое сообщение, вызов определенного метода. Под данными может подразумеваться все что угодно.
Такой стиль написания программ позволяет быстро реагировать на события. Для реализации паттерна наблюдатель создайте класс, содержащий данные и список наблюдателей и вызывайте метод оповещения всех наблюдателей при наступлении нового события.