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(obj);
}
public void notifyObjectModified(Object obj) {
for (Iterator<T> iter = (Iterator<T>) iterator(); iter.hasNext();)
iter.next().objectModified(obj);
}
}
Класс 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. Это действительно очень качественно спроектированная библиотека.
14 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 полю вывода переме?анных слов, который будет слу?ать событие нажатия кнопки.
Циклы, использующие iterator, красивее выглядят через foreach.
П.С. После препроцессора получится то же самое, но выглядит красивее.
Зачем такая сложная конструкция
for (Iterator iter = (Iterator) iterator(); iter.hasNext();)
iter.next().objectCreated();
}
Можно заменить на
for(T observer: Observers.this){
observer.objectCreated(obj);
}
Как насчет поддержки многопоточности и синхронизации списка нотифицируемых объектов?
Для воизбежания дедлоков нельзя вызывать нотификацию, залочив такой список…
Простей?ая реализация:
1) У каждого нотифицируемого объекта имеется счетчик ссылок, который при добавлении в список увеличивается а при удалении умень?ается;
2) Работа со списком контролируется каким-либо примитивом синхронизации;
3) При нотификации необходимо скопировать список под локом, при этом увеличив всем объектам счетчики ссылок (удобней всего использовать список аутопоинтеров, по типу ATL::CComPtr), убрать лок и нотифицировать слу?ателей по копии списка, после чего умень?ить счетчики ссылок.
в java.utils вроде бы есть класс Observable.