26
March

Паттерн Наблюдатель (Observer design pattern)

Posted in: Паттерны проектирования |

По просьбам читателей очередную статью о паттернах проектирования посвящу паттерну 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().objectCreated();
        }
    }

Класс 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. Это действительно очень качественно спроектированная библиотека.

1 Comment »

RSS feed for comments on this post.



LuLok
May 6, 2008 #

Спасибо! Доходчиво и понятно объяснил и показал *THUMBS_UP*

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>