mohuneko’s blog

かんばる駆け出しエンジニアのブログです

【Java】Observer パターン【デザインパターン】

駆け出しエンジニアがデザインパターンをもくもく勉強します

 こんな本で勉強しています🌟

目次

Observerパターンについて

Observerパターンとは

  • Observerパターンは、観察対象の状態が変化すると、観察者に対して通知が行われる方式です。
  • 状態変化に応じた処理を記述するときに有効です

サンプルプログラム

  • Observerパターンを使って、数を沢山生成するオブジェクトを観察者が観察して、その値を表示する例を取り上げます
  • 表示の方法は、観察者によって変わります
  • 各クラスの役割は以下のようになっています
名前 役割
Observer 観察者を表すインターフェース
NumberGenerator(Subject) 数を生成するオブジェクトを表す抽象クラス
RandomNumberGenerator(ConcreteSubject) ランダムに数を生成するクラス
DigitObserver(ConcreteObserver) 数字で数を表す行うクラスで、Observerインターフェースを実装する
GraphObserver(ConcreteObserver) 簡易グラフで数を表す行うクラスで、Observerインターフェースを実装

f:id:mohuNeko:20210101205015p:plain

Observerインターフェース

  • updateメソッドを呼びだすのは、数を生成するNumberGeneratorです
  • 自分の内容が変更されたことをObserberに伝えます
public interface Observer {
    public abstract void update(NumberGenerator generator);
}

NumberGeneratorクラス

  • 数を生成する抽象クラスです
    • 実際の数の生成と、取得はサブクラスが実装します
import java.util.ArrayList;
import java.util.Iterator;

public abstract class NumberGenerator {
    private ArrayList observers = new ArrayList();        // Observerたちを保持
    public void addObserver(Observer observer) {    // Observerを追加
        observers.add(observer);
    }
    public void deleteObserver(Observer observer) { // Observerを削除
        observers.remove(observer);
    }
    public void notifyObservers() {               // Observerへ通知
        Iterator it = observers.iterator();
        while (it.hasNext()) {
            Observer o = (Observer)it.next();
            o.update(this);
        }
    }
    public abstract int getNumber();                // 数を取得する
    public abstract void execute();                 // 数を生成する
}

RandomNumberGeneratorクラス

  • 乱数を生成します
import java.util.Random;

public class RandomNumberGenerator extends NumberGenerator {
    private Random random = new Random();   // 乱数生成機
    private int number;                     // 現在の数
    public int getNumber() {                // 数を取得する
        return number;
    }
    public void execute() {
        for (int i = 0; i < 20; i++) {
            number = random.nextInt(50);
            notifyObservers();
        }
    }
}

DigitObserverクラス

* 観察した値を数字で表示するためのものです

public class DigitObserver implements Observer {
    public void update(NumberGenerator generator) {
        System.out.println("DigitObserver:" + generator.getNumber());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

GraphObserverクラス

  • 観察した値を*****のように簡易グラフで表示します
public class GraphObserver implements Observer {
    public void update(NumberGenerator generator) {
        System.out.print("GraphObserver:");
        int count = generator.getNumber();
        for (int i = 0; i < count; i++) {
            System.out.print("*");
        }
        System.out.println("");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

Mainクラスで動作確認

public class Main {
    public static void main(String[] args) {
        NumberGenerator generator = new RandomNumberGenerator();
        Observer observer1 = new DigitObserver();
        Observer observer2 = new GraphObserver();
        generator.addObserver(observer1); //観察者登録
        generator.addObserver(observer2); 
        generator.execute(); //数を生成
    }
}

Observerパターンのメリット

  • 状態を持っているRandomNumberGeneratorクラスと、状態変化を通知してもらうDigitObserverクラス、GraphObserverクラスが登場します
    • その2つの役目を、ObserverインターフェースとNumberGeneratorクラスが繋ぎます
    • RandomNumberGeneratorクラスは、自分が現在監視しているの(通知する相手)が、DigitObserver/GraphObserverインスタンスなのかを知りません。
    • しかし、observersフィールドに格納されているインスタンスが、Observerインターフェースを継承していることは知っており、updateメソッドを呼び出せることが保証されています。
    • 一方、DigitObserverクラス、GraphObserverクラスは、自分を観察しているのがRandomNumberGeneratorインスタンスなのか、他のXXXNumberGeneratorインスタンスなのかを知らず、NumberGeneratorのサブクラスのインスタンスであり、getNumberメソッドを持っていることは知っています。
  • 具象クラスの交換可能性を高くするポイントは以下の2点です
    • 抽象クラスやインターフェースを使って、具象クラスから抽象メソッドを引きはがす
    • 引数でインスタンスを渡すときや、フィールドでインスタンスを保持するときは、抽象クラスやインターフェースの型にしておく
  • MVCモデルの中の、ModelとViewの関係は、 ObserverパターンのSubjectとObserverの関係に相当します
    • Modelは、"表示形式に依存しない内部モデルを操作する"役割で、Viewは "Modelをどのように見せるか" 管理します。一般的に、1つのModelに対して、複数のViewが対応します。

今日のポイント

  • Observerパターンは、観察対象の状態が変化すると、観察者に対して通知が行われる方式です
  • 状態を持っているクラス(ConcreteSubject)と、状態変化を通知してもらうクラスConcreteObserver)が登場します
  • お互いに通知する相手は知らないので、独立性を高めることができます。
  • 具象クラスの交換可能性を高くするには、以下が重要です
    • 抽象クラスやインターフェースを使って、具象クラスから抽象メソッドを引きはがす
    • 引数でインスタンスを渡すときや、フィールドでインスタンスを保持するときは、抽象クラスやインターフェースの型にしておく
  • MVCモデルの中の、ModelとViewの関係は、 ObserverパターンのSubjectとObserverの関係に相当します

 本日もお疲れ様です😊