デザインパターンを勉強してみる--第4回 Observerパターン--


Observerパターンとは

みなさんはネット通販を利用しますか?
当然のことですが、私たちが注文すればオンラインショップ側は受注します。
注文しなければ、受注しません。

このような、自分の状態を相手に伝え、その状態に応じた処理を相手にしてもらうパターン
Observerパターンです。

サンプルケース

では、注文~受注の流れをソースで表してみましょうか。

・注文ステータス(定数クラス)
 ※便宜上作りました。定数クラスがObserverパターンで必ず必要というわけではありません。

CommonConst.java
public final class CommonConst {
    //********************************************************************
    // 注文ステータス
    //********************************************************************
    // 未注文    
    public static final int ORDER_STATUS_UNORDERED = 0;

    // 注文済
    public static final int ORDER_STATUS_ORDERED = 1;
}

・注文の流れクラス(メインクラス)

Buying.java
public class Buying {
    public static void main(String[] args) {
        Customer cstmrA = new CustomerA();
        OnlineShop amazon = new Amazon();
        OnlineShop rakuten = new Rakuten();

        // 利用するお店は今回2店舗
        cstmrA.addOnlineShop(amazon);
        cstmrA.addOnlineShop(rakuten);

        // お客Aが注文
        cstmrA.order();
    }
}

・お客Aさんクラス

CustomerA.java
public class CustomerA extends Customer {
    //********************************************************************
    // インスタンス変数
    //********************************************************************
    // お客名
    private String customerName = "CustomerA";

    // 注文ステータス
    private int orderStatus = CommonConst.ORDER_STATUS_UNORDERED;

    //********************************************************************
    // メソッド
    //********************************************************************
    /**
     * 注文
     */ 
    @Override
    public void order() {
        System.out.println(customerName + ":商品を注文します。");

        // 注文ステータスを注文済に変更
        orderStatus = CommonConst.ORDER_STATUS_ORDERED;

        // 注文したことをオンラインショップに通知
        notifyOnlineShopOfOrder();
    }

    /**
     * 注文ステータス取得
     */ 
    @Override
    public int getOrderStatus() {
        return orderStatus;
    }

    /**
     * お客名取得
     */   
    @Override
    public String getName() {
        return customerName;
    }
}

・お客クラス(抽象クラス)

Customer.java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

public abstract class Customer {
    //********************************************************************
    // インスタンス変数
    //********************************************************************
    // オンラインショップ(顧客は複数のオンラインショップを利用するのでList型)
    private List<OnlineShop> onlineShops = new ArrayList<OnlineShop>();

    //********************************************************************
    // メソッド
    //******************************************************************** 
    /**
     * 利用するオンラインショップを登録
     */
    public void addOnlineShop(OnlineShop onlineShop) {
        onlineShops.add(onlineShop);
    }

    /**
     * 利用するオンラインショップから除外
     */   
    public void removeOnlineShop(OnlineShop onlineShop) {
        onlineShops.remove(onlineShop);
    }

    /**
     * 注文通知
     * -注文したことを各オンラインショップに通知して受注させる
     */ 
    public void notifyOnlineShopOfOrder() {
        // 利用するオンラインショップ全てに通知
        Iterator<OnlineShop> it = onlineShops.iterator();
        while (it.hasNext()) {
            // 受注
            it.next().receiveOrder(this);
        }
    }

    /**
     * 注文
     */ 
    public abstract void order();

    /**
     * 注文ステータス取得
     */     
    public abstract int getOrderStatus();

    /**
     * お客名取得
     */     
    public abstract String getName();
}

・Amazonクラス

Amazon.java
public class Amazon extends OnlineShop {
    //********************************************************************
    // インスタンス変数
    //********************************************************************
    private String onlineShopName = "Amazon";

    //********************************************************************
    // メソッド
    //******************************************************************** 
    /**
     * 受注
     */ 
    @Override
    public void receiveOrder(Customer customer) {
        if (customer.getOrderStatus() == CommonConst.ORDER_STATUS_ORDERED) {
            // お客が注文済なら受注扱いとする
            System.out.println(onlineShopName + ":" + customer.getName() + "様," 
                + onlineShopName + "がご注文を承りました。");            
        }
    }
}

・楽天クラス

Rakuten.java
public class Rakuten extends OnlineShop {
    //********************************************************************
    // インスタンス変数
    //********************************************************************
    private String onlineShopName = "楽天";

    //********************************************************************
    // メソッド
    //******************************************************************** 
    /**
     * 受注
     */ 
    @Override
    public void receiveOrder(Customer customer) {
        if (customer.getOrderStatus() == CommonConst.ORDER_STATUS_ORDERED) {
            // お客が注文済なら受注扱いとする
            System.out.println(onlineShopName + ":" + customer.getName() + "様," 
                + onlineShopName + "でございます。" + "確かにご注文を承りました。");            
        }
    }
}

・オンラインショップクラス(抽象クラス)

OnlineShop.java
public abstract class OnlineShop {
    //********************************************************************
    // メソッド
    //********************************************************************
    /**
     * 受注
     */ 
    public abstract void receiveOrder(Customer customer);
}

◆実行結果

CustomerA:商品を注文します。
Amazon:CustomerA様,Amazonがご注文を承りました。
楽天:CustomerA様,楽天でございます。確かにご注文を承りました。

処理の流れとしては、Buyingクラスの中でCustomerAに対しAmazon・Rakutenを登録し(cstmrA.addOnlineShop)、CustomerAのorderメソッドを実行しています。すると全オンラインショップに注文処理が行われます。
お客Aさんのようにお客はお客クラスを、各オンラインショップはオンラインショップクラスを継承するようなルールにしてあるので、ルールさえ守れば、お客とオンラインショップを更に定義することも可能です。(お客Bさんとかyahooとか。)例えばyahooを追加するならBuyingクラスの処理にcstmrA.addOnlineShop(yahoo);が増えますが、お客Aさんクラスには改修が入らず、簡単にオンラインショップを追加できます。

まとめ

このパターンでは、各オンラインショップをObserver(観察者)、お客AさんをSubject(対象者)といいます。"対象者"に何かあれば自身で"観察者"に連絡するというパターンです。Subjectは、一般に複数のObserverを登録できるように設計します。
使いどころの例としては、登録している複数の相手に対してデータを一斉配信するようなところで強みを発揮するそうです。

参考資料

http://www.techscore.com/tech/DesignPattern/Observer.html
https://ja.wikipedia.org/wiki/Observer_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
http://www.itsenka.com/contents/development/designpattern/observer.html
https://codezine.jp/article/detail/5327