Слайд 1Шаблоны проектирования
Design Pattern
Наблюдатель (Observer)
или
Слушатель (Listener)
по книге
Эрик и Элизабет Фримен
Паттерны проектирования
Слайд 2Объекты в курсе событий
Один объект оповещает другие объекты о наступлении неких
событий, которые могут представлять для них интерес, — причем объекты даже могут решать во время выполнения, желают ли они и дальше получать информацию.
Паттерн Наблюдатель чрезвычайно полезен и принадлежит к числу наиболее часто используемых паттернов JDK.
Также будут рассмотрены связи типа «один-ко-многим» и слабые связи.
Слайд 3
Техническое задание
разработка ПО метеорологической станции следующего поколения
Метеостанция работает на базе
запатентованного объекта WeatherData, отслеживающего текущие погодные условия (температура, влажность, атмосферное давление)
Требуется создать приложение, которое изначально отображает три визуальных элемента:
текущую сводку,
статистику
простой прогноз.
Все данные обновляются в реальном времени, по мере того как объект WeatherData получает данные последних измерений.
Необходимо предусмотреть возможность расширения программы.
Определите API, чтобы другие разработчики могли писать собственные визуальные элементы для отображения погодной информации и подключать их к приложению.
Слайд 4Обзор приложения Weather Station
Система состоит из 3-х компонентов:
Метеостанция – физическое устройство
осуществляет сбор данных
Объект WeatherData – отслеживает данные, обновляет отображаемую информацию
Экран – выводит текущую информацию о погоде
датчик температуры
датчик давления
Три режима:
текущее состояние
прогноз
статистика
Слайд 5Класс WeatherData
WeatherData
getTemperature()
getHumidity()
getPressure()
measurementsChanged()
// другие методы
Возвращают новейшие значения температуры, влажности, давления
Этот метод вызывается
при каждом обновлении датчиков
Метод measurementsChanged() необходимо реализовать так, чтобы он обновлял изображение для трех элементов:
текущего состояния,
статистики
прогноза.
/*
*
* метод вызывается при
* каждом обновлении датчиков
*
*/
public void measurementsChanged(){
// ваш код
}
Слайд 6Что известно?
Класс WeatherData содержит get-методы для показаний трех датчиков: температуры, влажности
и атмосферного давления.
Метод measurementsChanged() вызывается при появлении новых метеорологических данных.
Необходимо реализовать три экрана вывода, использующих метеорологические данные: экран текущего состояния, экран статистики и экран прогноза. Эти экраны должны обновляться каждый раз, когда у объекта WeatherData появляются новые данные.
Система должна быть расширяемой — другие разработчики будут создавать новые экраны вывода, а пользователи могут добавлять и удалять их в своих приложениях.
getTemperature()
getHumidity()
getPressure()
measurementsChanged()
Слайд 7Первое, что приходит в голову
Слайд 8Может, все же воспользуемся паттерном наблюдатель?
Чем плоха такая реализация?
public class WeatherData
{
// Объявления переменных экземпляров
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
}
Слайд 9Знакомство с паттерном Наблюдатель
Издатель открывает свое дело и начинает выпускать газету.
Вы
оформляете подписку у конкретного издателя. Каждый раз, когда выходит новый номер, он доставляется вам. Пока подписка действует, вы получаете новые выпуски газеты.
Если вы не хотите больше получать газету, вы прекращаете подписку.
Пока газета продолжает публиковаться, люди, гостиницы, авиалинии и т. д. постоянно оформляют и прекращают подписку.
Слайд 10Издатели + Подписчики = Паттерн Наблюдатель
Subject
Observers
Hey! Subscribe me!
I want to
listen you!
I’ve been updated!
I’ve been updated!
I’ve been updated!
I’ve been updated!
Hey! Unsubscribe me!
I don’t want to listen you anymore!
Слайд 11Определение паттерна Наблюдатель
Паттерн Наблюдатель определяет отношение «один-ко-многим таким образом, что при
изменений состояния одного объекта происходит автоматическое оповещение и обновление всех зависимых объектов.
Слайд 12Сила слабых связей
Если два объекта могут взаимодействовать, не обладая практически
никакой информацией друг о друге, такие объекты называют слабосвязанными.
Единственное, что знает субъект о наблюдателе, — то, что тот реализует некоторый интерфейс (Observer).
Новые наблюдатели могут добавляться в любой момент.
Добавление новых типов наблюдателей не требует модификации субъекта.
Субъекты и наблюдатели могут повторно использоваться независимо друг от друга.
Изменения в субъекте или наблюдателе не влияют на другую сторону.
Слайд 13Полезный принцип проектирования
На базе слабосвязанных архитектур строятся гибкие ОО-системы, которые хорошо
адаптируются к изменениям благодаря минимальным зависимостям между объектами
Слайд 14Проектирование Weather Station
реализация Weather Station
Слайд 15Встроенная реализация в языке Java
Интерфейс Observer и класс Observable пакета java.util
предоставляют значительное количество готовых функций
Со встроенной поддержкой Java достаточно расширить Observable и указать когда следует оповещать наблюдателей. API сделает все остальное
Слайд 16Переработанная версия Weather Station
Слайд 17Как встроенная поддержка паттерна работает в Java
Чтобы объект стал наблюдателем...
Как обычно,
реализуйте интерфейс Observer (на этот раз интерфейс java.util. Observer)
Чтобы поместить себя в список наблюдателей вызовите addObserver() у любого объекта Observable
Чтобы исключить себя из списка наблюдателей, вызовите у Observable метод deleteObserver().
Слайд 18Как встроенная поддержка паттерна работает в Java
Чтобы объект Observable рассылал оповещения...
Прежде
всего необходимо наделить его функциями субъекта, расширив суперкласс Java.util.Observable.
Далее процесс состоит из двух шагов:
Вызовите метод setChanged(), чтобы обозначить изменение состояния в вашем объекте.
Вызовите один из двух методов notifyObservers():
notifyObaervers()
notifyObservers(Object arg)
объект данных, передается всем наблюдателям
Слайд 19Как встроенная поддержка паттерна работает в Java
Чтобы объект Observer получал оповещения...
Он
должен, как и прежде, реализовать метод update, но сигнатурa этого метода несколько изменяется:
update(Observable o, Object arg)
Если данные должны «доставляться» наблюдателям, передайте их в объекте данных при вызове notifyObserver(arg).
Если нет, то наблюдатель должен «запросить» нужные данные от переданного ему объекта Observable
Слайд 20Зачем нужен метод setChanged() ?
setChanged(){
changed = true
}
notifyObservers(Object arg){
if (changed){
for every
observer on the list {
call update (this, arg)
}
changed = false
}
}
Метод setChanged() устанавливает флаг changed
notifyObservers() оповещает наблюдателей только при установленном флаге changed
После оповещения наблюдателей флаг changed сбрасывается
реализация Weather Station со встроенной поддержкой
Никогда не полагатесь на определенный порядок оповещения наблюдателей
Слайд 21Чем плох класс Observable
Observable представляет собой класс, а не интерфейс и
— что еще хуже — даже не реализацию интерфейса.
Нарушение принципа «программирование на уровне интерфейса, а не реализации»
поскольку Observable является классом, его необходимо субклассировать. Это означает, что поведение Observable невозможно добавить к существующему классу, который уже расширяет другой суперкласс.
при отсутствии интерфейса Observable вы даже не сможете создать собственную реализацию, которая хорошо сочетается со встроенным API Java. Также не удастся заменить реализацию java.util другой (скажем, многопоточной)
Нарушение принципа «отдавать предпочтение композиции перед наследованием »
В API класса Observable метод setChanged() объявлен защищенным.
Т.е. вы не сможете вызвать setChanged() без субклассирования Observable. Следовательно, вы даже не сможете создать экземпляр класса Observable и включить его в свои объекты посредством композиции — только субклассирование
Слайд 22Другие примеры паттерна Наблюдатель в Java
JavaBeans, Swing …
пример Swing
?
Слайд 23Резюме
Концепции ООП
Абстракция
Инкапсуляция
Полиморфизм
Наследование
Принципы
Инкапсулируйте, то что изменяется
Отдавайте предпочтение композиции перед наследованием
Программируйте на уровне
интерфейсов, а не реализации
Стремитесь к слабой связаности взаимодействующих объектов
Паттерны
Стратегия определяет семейство алгоритмов, инкапсулирует каждый из них и обеспечивает их взаимозаменяемость. Он позволяет модифицировать алгоритмы независимо от их использования на стороне клиента.
Паттерн Наблюдатель определяет отношение «один-ко-многим» таким образом, что при изменений состояния одного объекта происходит автоматическое оповещение и обновление всех зависимых объектов.
Слайд 24Ключевые моменты
Паттерн Наблюдатель определяет отношение «один-ко-многим» между объектами.
Субъекты обновляют наблюдателей через
единый интерфейс.
Субъект ничего не знает о наблюдателях — кроме того, что они реализуют интерфейс Observer.
При использовании паттерна возможен как запрос, так и активная доставка данных от субъекта (запрос считается более «правильным»).
Работа кода не должна зависеть от порядка оповещения наблюдателей.
Java содержит несколько реализаций паттерна Наблюдатель, включая обобщенную реализацию Java.util.Observable.
Помните о недостатках Java.util. Observable.
Не бойтесь самостоятельно реализовать Observable при необходимости.
Swing, как и многие GUI-инфраструктуры, широко применяет паттерн Наблюдатель.
Паттерн также встречается во многих других местах, включая JavaBeans и RMI.