2010年9月5日日曜日

Observerパターン

Observerとは、観察者のことです。Observerパターンとは、作業者の状態を監視する役のクラスがあるパターンです。


先に、監視されるクラス(作業をするクラス)を説明すると、コードとしては以下の通りです。

public abstract class Subject {
    private List<Observer> observerList = new ArrayList<Obsever>();

    public void addObserver(Observer observer) {
        observerList.add(observer);
    }

    protected void notifyObservers() {
    // 参考本ではこのメソッドはpublicになっていたが、子クラスからしか呼ばれないので、protectedにした。
        Iterator itr = observerList.iterator();
        while (itr.hasNext()) {
            Observer observer = (Observer) itr.next();
            observer.update(this);
        }
    }

    public abstract void execute();

    // その他、observerから呼んで欲しいメソッドを定義
}

まず、監視する人をこの人は知っています。複数人いてもよいようにListになっています。作業をするメソッドはexecute()で実装しますが、何か状態が変わったときはexecute()内でnotifyObservers()を呼び、監視者に通知します(Observer#update()を呼びます)

監視するクラスについて説明します。監視者は誰も作業者の状態変更通知を受け取れるようにupdate()を実装します。そーいうことで、Observerインターフェースを用意しておきます。

public interface Observer {
    void update(Subject subject);
}

Observerインターフェースの実装クラスは例えばこんな感じです。

public class SampleObserver implements Observer {
    public void update(Subject subject) {
        // Subjectクラスのメソッドを呼び、例えばコンソールに出力する、ログに残すなどの処理を行う。
    }
}

この後は、Subjectクラスの子クラスを作り、mainクラスからその子クラスのインスタンスを作って、SampleObserverをaddし、execute()を呼べばいいでしょう。

監視をするパターン自体は難しくないと思います。ポイントとしては、どのクラスでも作業者、監視者になれるよう、親クラス(厳密に言うと、作業者はabstractクラスで、監視者はinterface)を定義してあげて、具体的な作業者、監視者はそれらを継承・実装することで、汎用性があがるということです。

ただし、注意しなければならないのは、SubjectからObserverへ通知をした後に、ObserverからSubjectへ何かしらの指示を行った場合です。このとき、Observerからの指示でSubjectの状態が変化すると、またそのタイミングでObserverへ通知を行ってしまうからです。フラグでも用意して、防ぐようにしましょう。

ちなみに、Observer#update()をSubjectが呼ぶ際は、Subject自身を引数に入れています。が、例えば値の更新を通知するだけならば、引数に値を入れて渡すでも良いと思います。ただし、この場合において、もし、Observerが複数のSubjectを監視しているならば、どのSubjectからの変更通知なのか分からないです。Observerにどの程度の情報を渡すかは実装する際に検討すべきでしょう。


しかし、このパターンは監視というより、通知ですよね・・・


関連パターン
Mediatorパターン」Mediatorは、個々の作業者との調整役という位置づけであり、Observerでは、SubjectがObserverに通知をして同期を取ることに主眼を置いています。

0 件のコメント:

コメントを投稿