March
2008
По просьбам читателей очередную статью о паттернах проектирования посвящу паттерну Observer (Наблюдатель). Этот шаблон проектирования также известен под именами Dependents (Подчиненные) и Publisher-Subscriber (Издатель-Подписчик).
Реализация данного паттерна используется для наблюдения за состоянием объектов в системе. Если состояние объектов изменяется в процессе их жизненного цикла, то Наблюдатель оповещает другие части системы об этих событиях.
В данной статье я попытаюсь как можно проще и понятнее рассказать об этом паттерне и привести пример программного кода на Java, реализующего Observer.
В каких случаях используется Наблюдатель?
- Если один объект должен передавать сообщения другим объектам, но при этом он не может или не должен знать об их внутреннем устройстве;
- В случае если при изменении одного объекта необходимо изменять другие объекты;
- Для предотвращения сильных связей между объектами системы;
- Для наблюдения за состоянием определенных объектов системы;
Наблюдаемый (observable) объект (а точнее субъект или subject) должен предоставлять интерфейс для регистрации и дерегистрации наблюдателей (listeners).
Ну а сами наблюдатели должны как минимум обладать открытым методом через который и будет происходить оповещение об изменении состояния субъекта. Этот метод часто называют notify.
Так как наблюдателей может быть достаточно много, для упрощения работы с ними можно использовать коллекцию (collection of observers). Приблизительно такой код я использую в этом случае:
public interface Observer {
void objectCreated(Object obj);
void objectModified(Object obj);
}
class EmptyObserver implements Observer {
public void objectCreated(Object obj) { }
public void objectModified(Object obj) { }
}
class Observers<T extends Observer> extends ArrayList<T> {
public void notifyObjectCreated(Object obj) {
for (Iterator<T> iter = (Iterator<T>) iterator(); iter.hasNext();)
iter.next().objectCreated();
}
public void notifyObjectModified(Object obj) {
for (Iterator<T> iter = (Iterator<T>) iterator(); iter.hasNext();)
iter.next().objectModified();
}
}
Класс EmptyObserver может быть полезен в случае если у Наблюдателя достаточно большое количество notify методов. Тогда используя анонимные классы можно с легкостью создавать необходимые нам “узкоспециализированные” наблюдатели (те, у которых реализовано ограниченное количество методов) на лету:
Observers observers = new Observers();
observers.add(new EmptyObserver() {
public void objectCreated(Object obj) { /* реализация */ }
});
Далее, экземпляр класса Observers помещается в субъект за которым должно вестись наблюдение. Во всех местах кода, где происходит интересующее нас действие с классом-субъектом, добавляем соответствующий вызов notify метода у коллекции наблюдателей:
public class Subject {
Observers observers = new Observers();
private Object field;
public void setField(Object o) {
field = o;
observers.notifyObjectModified(this);
}
}
Кажется, основы осветил. Нюансы предлагаю обсуждать в комментариях.
Классический пример использования паттерна Observer - это классы из пакета Java Swing. В Swing паттерн Наблюдатель используется для организации слабой взаимозависимости между моделью и графическими объектами. Методы notify вызываются при изменении значений свойств модели и передают информацию в виджеты.
Кстати, очень рекомендую всем посмотреть исходный код Java Swing. Это действительно очень качественно спроектированная библиотека.
9 Comments »
RSS feed for comments on this post.
Спасибо! Доходчиво и понятно объяснил и показал *THUMBS_UP*
Еще вопрос: что вот это за структура такая?
…
class Observers extends ArrayList {
public void notifyObjectCreated(Object obj) {
for (Iterator iter = (Iterator) iterator(); iter.hasNext();)
…
Очень похоже на шаблоны в С++, но в то же время что-то другое.. .может какуюнить статейку на эту тему или хотя бы где можно об этом почитать..
Спасибо
угловые скобки и их содержимое не пропечатались…
class Observers<T extends Observer> extends ArrayList<T> {
public void notifyObjectCreated(Object obj) {
for (Iterator<T> iter = (Iterator<T>) iterator(); iter.hasNext();)
iter.next().objectCreated();
}
Это параметризируемые типы (generic).
Скоро напишу статью на эту тему.
в методе notifyObjectModified наверное должно быть не iter.next().objectCreated(), а iter.next().objectModified()
У вас в коде возможно ошибка в методе notifyObjectModified. Возможно нужно вызвать iter.next().objectModified() вместо iter.next().objectCreated()?
Да, спасибо, исправил.
Написанный кода выполняет следущую фукцию: мы вводим текст в окошко ввода(используется класс генерации случайных чисел Random), и в окошке вывода получаем измененный текст, то есть слова мешаются. Вопрос: Возможно и разумно в этом случае использовать OBSERVER?
Если я правильно понимаю, Вы используете некоторую библиотеку (например Swing) для построения GUI и перемешивание слов должно осуществлятся в реальном времени после нажатия клавиш.
Если так, то нужно использовать Listener (это и есть observer), а точнее добавить listener полю вывода перемешанных слов, который будет слушать событие нажатия кнопки.