先に、監視されるクラス(作業をするクラス)を説明すると、コードとしては以下の通りです。
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 件のコメント:
コメントを投稿