Шаблон проектирования Наблюдатель (Observer)

Рассмотрим третий шаблон проектирования.

Суть шаблона — изменения в одном объекте порождают изменения во многих других.

Здесь есть 2 понятия — Наблюдатель (observer) и наблюдаемый (subject). В этом шаблоне наблюдателей несколько, а наблюдаемый один.

Перейдем от слов к коду.

Рассмотрим данный шаблон на примере типичной задачи в андроид. Имеется 3 поля ввода и одна кнопка, при нажатии на кнопку необходимо проверить, что поля ввода не пусты перед тем как например отправить на сервер данные (пример очень упрощен).

Для этих целей нам понадобятся интерфейсы наблюдателя и наблюдаемого.

У интерфейса наблюдателя всего 2 метода — обновление, когда произошло изменение и установка наблюдаемого.

Теперь рассмотрим интерфейс самого наблюдаемого.

У наблюдаемого 4 метода, регистрировать наблюдателя чтобы следить за изменениями и отписываться от изменений (unregister), подобно подписке например в YouTube и отписке от новых видео. Далее у нас метод, который отвечает за то, что все наблюдатели получат оповещение об изменении (notifyObservers) и один метод (getUpdate) для того, чтобы каждый из наблюдателей получил само обновление (в сигнатуре метода Object для удобного изменения в дальнейшем).

А теперь рассмотрим реализации этих интерфейсов.

В нашем случае наблюдатель будет принимать параметром само поле ввода, метод установки наблюдаемого самый обычный сеттер. А в методе обновления получаем от наблюдаемого сообщение (об ошибке) после чего проверяем заполнено ли поле ввода, если нет — показываем ошибку, если же польователь заполнил поле — убираем ошибку (здесь можно бы было устанавливать наблюдаемое в самом конструкторе).

А теперь посмотрим на имплементацию наблюдаемого. Так как он у нас будет в единственном числе, необходимо все методы синхронизировать (т.е. ставить в очередь на исполнение, чтобы избежать коллизий). Итак, у наблюдаемого 4 поля: список наблюдателей, так как их будет много, сообщение, которое мы передадим из конечного класса, флаг о том, что произошли изменения и сам мютекс для синхронизации. В методе регистрации наблюдателя проверяем на пустоту и если его нет в списке наблюдателей, то добавляем. В методе отписки точно также проверяем на содержание в списке и убираем из него (все конечно же синхронизированно).

В методе обновления всех наблюдателей необходимо проверить на изменения, если их нет, то и делать ничего не нужно. Если были изменения, то нужно скопировать наблюдатели в локальный список и вызвать у всех метод обновления. Метод получения сообщения обычный геттер. И последний метод, который будем вызывать в конечном классе — postMessage, где мы устанавливаем сообщение об ошибке, меняем флаг о том, что произошли изменения и вызываем метод обновления всех наблюдателей.

Рассмотрим разметку конечного класса.

Здесь все просто — 3 поля ввода и кнопка на дне.

Перейдем к самому классу.

В методе создания экрана инициализируем поля ввода, создаем 3 наблюдателя и наблюдаемое и всем наблюдателям устанавливаем одно и то же наблюдаемое. В методе onResume подписываем всех наблюдателей на наблюдаемое, а в методе onPause отписываем. При нажатии на кнопку постим сообщение ошибки, которое будет отображаться если поля ввода пустые,  далее этот метод вызывает методы обновления у всех наблюдателей и они проверяют на пустоту поля ввода.

Цепочка действий такая onButtonClick (postMessage) -> notifyObservers -> update

Посмотрим на скриншотах как это все выглядит.

Три пустых поля и кнопка, при нажатии у всех полей появится сообщение об ошибке.

Теперь давайте заполним фамилию и имя и посмотрим что будет.

При последующей проверке у 2 из 3 наблюдателей проверка на пустоту прошла и сообщение об ошибке убралось, а на третьем осталась.

Повторю, это очень упрощенный пример, хоть и имеет право на жизнь.

В реальности же у вас могут быть разные поля ввода с разными ошибками, также необходимо будет перед отправкой данных на сервер вернуть в методе postMessage некий булев флаг о том, что ошибок нет и можно отправлять введенные данные. Для этого можно поменять сигнатуры методов update и notifyObservers, чтобы они возвращали флаг отсутствия ошибки.

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

Минусы незначительны на мой взгляд, нужно не забывать отписываться от изменений, чтобы избежать утечек памяти.

Запись опубликована в рубрике Программирование Android, Программирование Java. Добавьте в закладки постоянную ссылку.