Java観察者デザインモード(ObservableとObserver)


観察者モードは、複数の観察者オブジェクトが同時にある主題オブジェクトを傍受するように、一対の複数の依存関係を定義する。
このテーマのオブジェクトが状態に変化した場合、すべての観察者に、自動的に自分自身を更新することができるように通知します。
一、観察者パターン紹介
JavaではObservable類とObserverインターフェースにより観察者モードを実現した。一つのObserverオブジェクトはObservableオブジェクトの変化を監視しています。Observableオブジェクトが変化した場合、Observerは通知を得て、対応する作業を行うことができます。
画面Aがデータベース内のデータを表示し、画面Bがデータベース内のデータを修正したら、画面Aが再度ロードされる。この時は観察者モードが使えます。
二、観察者モードの実現方法
java.util.Observableの中には2つの方法がObserverにとって特に重要です。
①set Changed()方法

/** 
* Sets the changed flag for this {@code Observable}. After calling 
* {@code setChanged()}, {@code hasChanged()} will return {@code true}. 
*/ 
protected void setChanged() { 
changed = true; 
} 
②notifyObservers()   / notifyObservers(Object data)  
[java] view plaincopy
/** 
* If {@code hasChanged()} returns {@code true}, calls the {@code update()} 
* method for every observer in the list of observers using null as the 
* argument. Afterwards, calls {@code clearChanged()}. 
* <p> 
* Equivalent to calling {@code notifyObservers(null)}. 
*/ 
public void notifyObservers() { 
notifyObservers(null); 
} 
/** 
* If {@code hasChanged()} returns {@code true}, calls the {@code update()} 
* method for every Observer in the list of observers using the specified 
* argument. Afterwards calls {@code clearChanged()}. 
* 
* @param data 
* the argument passed to {@code update()}. 
*/ 
@SuppressWarnings("unchecked") 
public void notifyObservers(Object data) { 
int size = 0; 
Observer[] arrays = null; 
synchronized (this) { 
if (hasChanged()) { 
clearChanged(); 
size = observers.size(); 
arrays = new Observer[size]; 
observers.toArray(arrays); 
} 
} 
if (arrays != null) { 
for (Observer observer : arrays) { 
observer.update(this, data); 
} 
} 
} 
以上の二つの方法はとても重要です。
set Changed()方法――
内部フラグを設定するために使用します。データが変化しました。
notifyObservers()方法/notifyObservers(Object data)方法――
すべてのObserverデータに変化が発生したことを通知します。この場合、Observerは自動的に複写済みのudate(Observable observable、Object data)を呼び出して、いくつかの処理を行います。
Observerには二つの方法があります。一つは無参で、一つは参加があります。このパラメータは何の効果がありますか?
その中の一つの役割:今はすべてのObserverに通知したくなくて、その中の一つの指定されたObserverだけを処理したいなら、一つのパラメータをIDとして渡すことができます。そして、Observerの中で判断します。各Observerは最後のパラメータIDを受信するだけが自分のものと判断します。
もちろんパラメータには他の作用があります。例をあげただけです。
次の例を挙げて説明します。

import java.util.Observable; 
/** 
*       
*/ 
public class SimpleObservable extends Observable 
{ 
private int data = 0; 
public int getData(){ 
return data; 
} 
public void setData(int i){ 
if(this.data != i) { 
this.data = i; 
setChanged(); 
//   setChange()    ,notifyObservers()     update(),       。 
notifyObservers(); 
} 
} 
} 
上記のクラスは観察者クラスで、Observable類を継承しています。このクラスは観察可能です。
そしてsetData()メソッドでは、データが変化したところでObservable類のset Chend()メソッドとnotifyObservers()メソッドを呼び出して、データが変化したことを示し、すべてのObserverにそれらのudate()メソッドを呼び出して、いくつかの処理を行います。
注意:set Change()が呼び出された後だけ、notifyObservers()はudate()を呼び出します。そうでないと何もしません。

/** 
*      
*/ 
public class SimpleObserver implements Observer 
{ 
public SimpleObserver(SimpleObservable simpleObservable){ 
simpleObservable.addObserver(this ); 
} 

public void update(Observable observable ,Object data){ // data     ,       
System.out.println(“Data has changed to” + (SimpleObservable)observable.getData()); 
} 
} 
観察者(SimpleObservable類)の例を生成することにより、addObserver(this)方法を呼び出して、観察者(SimpleObserver類)を観察対象者(SimpleObservable類)に到達させる。
そして、アップロード方法を復唱して、データを変えた後の処理をします。
簡単なテストクラスを書いてテストしてもいいです。

public class SimpleTest 
{ 
public static void main(String[] args){ 
SimpleObservable doc = new SimpleObservable (); 
SimpleObserver view = new SimpleObserver (doc); 
doc.setData(1); 
doc.setData(2); 
doc.setData(2); 
doc.setData(3); 
} 
} 

運転結果は以下の通りです
Data has changed to 1
Data has changed to 2/第二回setData(2)の時はset Changeがないため、udateは呼び出しられませんでした。
Data has changed to 3
Observableの他のいくつかの属性と方法を紹介します。
属性――
//observersはListで、通知するすべてのobserverを保存しています。
List<Observer>observers=new ArayList<Observer>();
//changedはbootlean型のフラグビットで、データが変わったかどうかを示しています。
bollan changed=false;
方法――

//     Observer   observers  
public void addObserver(Observer observer) { 
if (observer == null) { 
throw new NullPointerException(); 
} 
synchronized (this) { 
if (!observers.contains(observer)) 
observers.add(observer); 
} 
} 
//    observers     observer 
public synchronized void deleteObserver(Observer observer) { 
observers.remove(observer); 
} 
//     observers 
public synchronized void deleteObservers() { 
observers.clear(); 
} 
//     observers observer    
public int countObservers() { 
return observers.size(); 
} 
//               
protected void clearChanged() { 
changed = false; 
} 
//               
protected void setChanged() { 
changed = true; 
} 
//         
public boolean hasChanged() { 
return changed; 
} 
//     observer(  ) 
public void notifyObservers() { 
notifyObservers(null); 
} 
//     observer(  ) 
@SuppressWarnings("unchecked") 
public void notifyObservers(Object data) { 
int size = 0; 
Observer[] arrays = null; 
synchronized (this) { 
if (hasChanged()) { 
clearChanged(); 
size = observers.size(); 
arrays = new Observer[size]; 
observers.toArray(arrays); 
} 
} 
if (arrays != null) { 
for (Observer observer : arrays) { 
observer.update(this, data); 
} 
} 
} 
注意:Observer対象を廃棄する前に必ずdeleteObserverでリストから削除します。つまり、one Destroy()メソッドでdeleteObserver()を呼び出します。
さもなくばまだ対象の引用の関係があるため、Observer対象はごみに収集されないで、メモリが漏れていることをもたらして、しかもすでに死んだObserverは依然として知らせられて、予想外の誤りをもたらすかもしれなくて、その上リストがますます大きくなることに従って、notifyObserver操作もますます遅くなります。
観察者モードに関わるキャラクターは、
●抽象的なテーマ(Subject)キャラクター:抽象的なテーマキャラクターは、観察者の対象に対するすべての引用を一つの集まり(例えば、ArayListオブジェクト)に保存し、各テーマごとに任意の数の観察者を持つことができる。抽象的なテ-マは一つのインタフェースを提供して、観察者の対象を増加して削除することができて、抽象的なテ-マの役はまた抽象的な被観察者(Observable)の役ともいいます。
●具体的なテーマ(Cocrete Subject)キャラクター:具体的な観察者の対象に状態を格納する。具体的なテーマの内部状態が変わると、登録したすべての観察者に通知します。具体的なテーマキャラクターは、具体的な観察者役とも言われています。
●抽象的観察者(Observer)役割:すべての具体的観察者のために一つのインターフェースを定義し、テーマの通知を得た時に自分を更新する、このインターフェースを更新インターフェースといいます。
●具体的な観察者キャラ:テーマと格納されている状態が自分に合っている状態。具体的な観察者の役は抽象的な観察者の役の要求のを実現してインタフェースを更新して、自分の状態とテ-マの状態を調和させます。必要であれば、具体的な観察者の役割は、特定のテーマオブジェクトに向けた参照を維持することができる。