書きたいことを書きたいだけ。

とある会社員の生活記録。

デザインパターン「Observer」

ここに書いてある内容は個人のアウトプットを吐き出している場です。 違うことが多々あると思いますが、温かい目で見てください。

Observerパターン

どんな時にObserverが役に立つのか?

Observerとは日本語で「観察者」の意味です。
じゃあ一体何を観察するのかというと、例えばある数値を吐き出すセンサーとか。
あるオブジェクトの状態が変化した時に、それに依存しているすべてのオブジェクトに通知を行いたいときに使います。

f:id:itisyuu:20190416143952p:plain

クラス図で表すとこんな感じです。 具象サブジェクト(ConcreteSubject)はSubjectインタフェースの実装を行い、具象サブジェクトは具象オブザーバー(ConcreteObserver)の登録・削除メソッドやオブザーバーを更新するためのメソッドを持っています。

メリット

新しいオブザーバを追加できる 新しい種類のオブザーバを追加するために、サブジェクトの変更が必要ない

実例

車速センサーとスピードモニターとカーナビ

今回は、自動車の車速センサーについて考えてみようと思います。
車速センサーと自動車のスピードモニターは繋がっており、車速が変化したタイミングでスピードモニターが情報を出力するという流れです。 更に車速センサーはカーナビにも繋がっているとしましょう。
つまり、この場合はスピードモニターとカーナビは「Observer(観察者 )」です。
逆に、車速センサーは情報を出力or観察される側なので「Subject」と呼びます。
(実際には車速センサーはECUとかと繋がってるんだけどご愛嬌)

この場合のクラス図はこのように置き換えられます。

f:id:itisyuu:20190416150749p:plain

SubjectとObserverインタフェースの作成

public interface Observer {
    public void update(float speed);
}

public interface Subject {
    public void registerObserver(Observer o); //Observerを引数としてObserverを登録
    public void removeObserver(Observer o); // //Observerを引数としてObserverを削除
    public void notifyObserver(); //状態が変わった時にObserverに通知
}

車速センサー(SpeedSensor)の実装を行ってみる

import java.util.ArrayList;

public class SpeedSensor implements Subject{
    private float speed;
    private ArrayList observers;
    
        public SpeedSensor(){
        observers = new ArrayList();//コンストラクタで呼ばれた時にnewしておく
    }

    public void registerObserver(Observer o) {
        observers.add(o);
    }
    public void removeObserver(Observer o) {
        observers.remove(o);
    }
    public void notifyObserver() {
        for(int i=0; i<observers.size();i++){
            Observer observer = (Observer)observers.get(i);
            observer.update(speed);
        }
    }
    public void setSpeed(float speed){
        this.speed = speed;
        notifyObserver();//スピードが変わったのでObserverに通知
    }
}

となります。
ArrayListとしてObserver用のリストを保持して、登録や削除を行っていきます。
そして、変更が起こったことを通知する際には、リストを順番に呼び出してupdate()を実行していきます。
Observerインタフェースでupdate()を定義しているので、Observerは絶対にupdate()を実装していることが分かっています。
そのため、順番にupdate()を呼び出して通知を行うことが出来るのです。

SpeedMetorの実装

public class SpeedMetor implements Observer{
    private float speed;
    private Subject speedSensor;
    public SpeedMetor(Subject speedSensor){
        this.speedSensor = speedSensor;
        speedSensor.registerObserver(this);
    }
    public void update(float speed) {
        this.speed = speed;
        System.out.println("SpeedMetor Now : "+ speed);
    }
}

コンストラクタにはSubjectであるspeedSensorが渡されてspeedSensorの通知リスト(要はArrayList observers)にObserverであるSpeedMetorを登録します。
Subject(この場合だとSpeedSensor)からupdate()が呼び出されるとspeedを変数に保存して、出力を行います。

CarNavigationSystemの実装

SpeedMonitorと同じです。出力のところをCarnaviに変えています。

public class CarNavigationSystem implements Observer{
    private float speed;
    private Subject speedSensor;
    public CarNavigationSystem(Subject speedSensor){
        this.speedSensor = speedSensor;
        speedSensor.registerObserver(this);
    }   
    public void update(float speed){
        this.speed = speed;
        System.out.println("Carnavi Speed :"+ speed);
    }
}

実際に試してみる

public class Main {
    public static void main(String[] args){
    
        SpeedSensor speedSensor = new SpeedSensor();//Subjectのオブジェクト作成
        
        SpeedMetor speedMetor = new SpeedMetor(speedSensor);//Observerのオブジェクト作成
        CarNavigationSystem carnavi =
                new CarNavigationSystem(speedSensor);
        
        speedSensor.setSpeed(11.2f);
        speedSensor.setSpeed(12.1f);
    }
}

実行結果は以下の通りです。

SpeedMetor Now : 11.2
Carnavi Speed :11.2
SpeedMetor Now : 12.1
Carnavi Speed :12.1

SpeedMetorとCarnaviにSpeedSensorの値が通知されていることが確認できます。
今回はObserver側のオブジェクトが2つだけでしたが、Observerには何個も追加することが出来ます。

まとめ

Observer パターン(オブザーバ・パターン)とは、プログラム内のオブジェクトのイベント( 事象 )を他のオブジェクトへ通知する処理で使われるデザインパターンの一種。 通知するオブジェクト側が、通知されるオブジェクト側に観察(英: observe)される形になる事から、こう呼ばれる。

これはwikiからの引用です。 また付け加えるなら、Observerパターンはオブジェクト間の1対多をの依存関係を定義しています。そして「1」側のオブジェクトの変化が、依存している「多」側のオブジェクトに対して通知を行っていることがわかります。