Observer パターン (あるオブジェクトの変化を, それに依存するオブジェクトに自動的に知らせる仕組みを提供する)

[Observer] = [観察者]

このパターンは, 観察者となるオブジェクトが, 観察対象となるオブジェクトからの状態変化の通知を受けそれに体する処理を行うパターン.

役割

  1. Subject (観察対象者):
[Observer] の観察対象となるオブジェクトのインタフェースを定義する. 観察者を保持するメソッド, 観察者への通知メソッド (状態が変化した際に [Observer] に通知) 等を定義する. 保持する観察者は複数でも可能.
  1. ConcreteSubject[A|B] (具体的な観察対象者):
[Subject] で定義したインタフェースを実装する. [Observer] への通知は, 自身に保持している [Observer] オブジェクトの通知受信用メソッドを呼び出すことにより実現する.
  1. Observer (観察者):
[Subject] の状態変化を監視する. (実際には, 各 [Subject] からの通知を処理する) [Subject] からの通知 (状態変化等の情報) 受信用のメソッド (update) のインタフェースを定義する.
  1. ConcreteObserver (具体的な観察者):
[Observer] で定義したインタフェースを実装する. [Subject] から状態受信用のメソッドの呼び出しが行われると, その呼出元の状態を元に処理を行う.
  1. Client (利用者):
[Observer] パターンを適用したクラスを用い処理を行う.

クラス図

Observer パターンのクラス図

_images/designpattern-observer011.gif

ソースコード

  1. Subject.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

public abstract class Subject{
    private List<Observer> observers = new ArrayList<Observer>(); // 複数の観察者の保持が可能
    private Random random = new Random();

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

    public void removeObserver(Observer observer){
	observers.remove(observer);
    }

    public void notifyObservers(){
	Iterator<Observer> it = observers.iterator();
	while(it.hasNext()){
	    it.next().update(this);
	}
    }

    public int getRandomNumber(){
	return random.nextInt(100);
    }

    public abstract int getStatus();
    public abstract String getName();
    public abstract void run();
}

2-1. ConcreteSubjectA.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ConcreteSubjectA extends Subject{
    private String subjectName = "ConcreteSubjectA";
    private int status = 0;

    public int getStatus(){
	return status;
    }

    public String getName(){
	return subjectName;
    }

    public void run(){
	int randomNumber = 0;
	randomNumber = getRandomNumber();
	if(status < randomNumber){
	    System.out.println(subjectName + "の状態が" + status + "->" + randomNumber + "に変わる");
	    status = randomNumber;
	    notifyObservers();
	}
    }
}

2-2. ConcreteSubjectB.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ConcreteSubjectB extends Subject{
    private String subjectName = "ConcreteSubjectB";
    private int status = 100;
    
    public int getStatus(){
	return status;
    }

    public String getName(){
	return subjectName;
    }

    public void run(){
	int randomNumber = 0;
	randomNumber = getRandomNumber();
	if(status > randomNumber){
	    System.out.println(subjectName + "の状態が" + status + "->" + randomNumber + "に変わる");
	    status = randomNumber;
	    notifyObservers();
	}
    }
}
  1. Observer.java
1
2
3
public abstract class Observer{
    public abstract void update(Subject subject);
}
  1. ConcreteObserver.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

public abstract class Subject{
    private List<Observer> observers = new ArrayList<Observer>(); // 複数の観察者の保持が可能
    private Random random = new Random();

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

    public void removeObserver(Observer observer){
	observers.remove(observer);
    }

    public void notifyObservers(){
	Iterator<Observer> it = observers.iterator();
	while(it.hasNext()){
	    it.next().update(this);
	}
    }

    public int getRandomNumber(){
	return random.nextInt(100);
    }

    public abstract int getStatus();
    public abstract String getName();
    public abstract void run();
}
  1. Client.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Client{
    public static void main(String[] args){
	Observer ob = new ConcreteObserver();

	Subject sb1 = new ConcreteSubjectA();
	Subject sb2 = new ConcreteSubjectB();

	sb1.addObserver(ob);
	sb2.addObserver(ob);

	for(int i = 0; i < 50; i++){
	    sb1.run();
	    sb2.run();
	    try{
		Thread.sleep(500);
	    }catch(InterruptedException e){
		e.printStackTrace();
	    }
	}
	System.out.println("**** END ****");
    }
}

上記のプログラムの実行結果:

[wtopia Observer]$ java Client
ConcreteSubjectAの状態が0->61に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectA
状態: 61
ConcreteSubjectBの状態が100->42に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectB
状態: 42
ConcreteSubjectAの状態が61->74に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectA
状態: 74
ConcreteSubjectAの状態が74->94に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectA
状態: 94
ConcreteSubjectBの状態が42->10に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectB
状態: 10
ConcreteSubjectAの状態が94->99に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectA
状態: 99
ConcreteSubjectBの状態が10->5に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectB
状態: 5
ConcreteSubjectBの状態が5->4に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectB
状態: 4
**** END ****
[wtopia Observer]$ java Client
ConcreteSubjectAの状態が0->91に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectA
状態: 91
ConcreteSubjectBの状態が100->69に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectB
状態: 69
ConcreteSubjectBの状態が69->21に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectB
状態: 21
ConcreteSubjectBの状態が21->3に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectB
状態: 3
ConcreteSubjectAの状態が91->99に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectA
状態: 99
ConcreteSubjectBの状態が3->0に変わる
観察者: ConcreteObserverA
通知者: ConcreteSubjectB
状態: 0
**** END ****