6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

にゃんこで分かるObserverパターン 後編

Last updated at Posted at 2018-02-20

#概要
前回は、飼いねこと野良ねこの2匹を用いて、Observer パターンの構造を説明しました。今回はObserver.update 関数を中心に、にゃんこがごはんだけでなく他のものをねだるようになります。前回の内容を前提として解説いたしますので、先にそちらをご覧ください。

IMG_0756.JPG
(ちょっとボケてますが、庭をのぞきこむ実家のにゃんこ)

#登場するにゃんこ

飼い猫のタマ
タマは、マンションの3階で暮らしています。飼い主は一人暮らしです。

cat_matatabi.png

飼い猫のモモ
最近、タマと同じ家に住むようになりました。

cat_scottish_fold.png

#ごはんだけでは満足できないタマ
タマが飼い主におねだりするのは、ごはんだけではありません。遊んでほしかったりマタタビが気になったりします。
飼い主にどのお願いをしているかを伝える方法は、おおまかに2つあります。引数を介して渡すPush型と、引数を介さないで(その他の方法を用いて)渡すPull型です。
前回のコードは引数がなかったため、Pull型に分類されます。引数を受け取る必要がない処理の場合はPull型を用いるといいでしょう。もしPull型で必要な情報がある場合は、Observer クラスが集めたり、自身が知っているものを用いたりします。
一方、Push型では引数に、文字列や数値、Event クラス(後述)などを渡します。

#飼い主にお願いを伝える
まずは、様々なおねだりをできるようにするために「お願い」をクラスにします。

Request.java
public abstract class Request{// Event クラス
    public abstract void words();
}
Food.java
public class Food extends Request{// Event クラス
    @Overide
    public void words(){
        System.out.println("にゃー(おなかすいた)");
    }
}
Playing.java
public class Playing extends Request{// Event クラス
    @Overide
    public void words(){
        System.out.println("にゃー(ひまー)");
    }
}
Matatabi.java
public class Matatabi extends Request{// Event クラス
    @Overide
    public void words(){
        System.out.println("にゃー(マタタビほしい!)");
    }
}

uml4.png

Event クラスを渡す

次の例はPush型で、Request クラス(=Event クラス) を引数にしたコードです。

Cat.java
public class Cat{// Subject クラス
    private Human human;
    
    public void setHuman(Human human){// setObserver()
        this.human = human;
    }

    public void hungry(){// notifyObservers()
        this.human.called(new Food());
    }

    public void killTime(){// notifyObservers()
        this.human.called(new Playing());
    }

    public void wantMatatabi(){// notifyObservers()
        this.human.called(new Matatabi());
    }
}
Human.java
public class Human{// Observer クラス
    public void called(Request request){// notify() or update()
        request.words();

        String className = request.getClass().getName();
        if(className.equals(Food.class.getName())){
            System.out.println("カリカリをあげる");

        } else if(className.equals(killTime.class.getName())) {
            System.out.println("もう、しょうがないなー(ねこじゃらしフリフリ)");

        } else if(className.equals(Matatabi.class.getName())) {
            System.out.println("マタタビをあげる");
        }
    }
}
Main.java
    public static void main(String[] args) {
        Cat tama = new Cat();
        Human master = new Human();
        tama.setHuman(master);

        tama.hungry();
        tama.killTime();
        tama.wantMatatabi();
    }
にゃー(おなかすいた)
カリカリをあげる
にゃー(ひまー)
もう、しょうがないなー(ねこじゃらしフリフリ)
にゃー(マタタビほしい!)
マタタビをあげる

Subject クラス自身(this)を渡す

よく渡す値として、呼び出し元のSubject クラス(=Cat クラス)自身もあります。Observer クラス(=Human クラス)が複数のSubject クラスから呼び出される場合、呼び出し元が分かるため便利です。

Cat.java
public class Cat{// Subject クラス
    private Human human;
    private Request request;
    public final String name;
    
    public Cat(String name){
        this.name = name;
    }

    public void setHuman(Human human){// setObserver()
        this.human = human;
    }

    public void hungry(){// notifyObservers()
        request = new Food();
        this.human.called(this);
    }

    public void killTime(){// notifyObservers()
        request = new Playing();
        this.human.called(this);
    }

    public void wantMatatabi(){// notifyObservers()
        request = new Matatabi();
        this.human.called(this);
    }

    public Request getRequest(){
        return request;
    }
}
Human.java
public class Human{// Observer クラス
    public void called(Cat cat){// notify() or update()
        cat.getRequest().words();

        String className = cat.getRequest().getClass().getName();
        if(className.equals(Food.class.getName())){
            System.out.println(cat.name + "にカリカリをあげる");

        } else if(className.equals(killTime.class.getName())) {
            System.out.println("もう、しょうがないなー(ねこじゃらしフリフリ)");

        } else if(className.equals(Matatabi.class.getName())) {
            System.out.println(cat.name + "にマタタビをあげる");
        }
    }
}
Main.java
    public static void main(String[] args) {
        Cat tama = new Cat("タマ");
        Cat momo = new Cat("モモ");
        Human master = new Human();
        tama.setHuman(master);
        momo.setHuman(master);

        tama.hungry();
        tama.killTime();
        momo.hungry();
        tama.wantMatatabi();
    }
にゃー(おなかすいた)
タマにカリカリをあげる
にゃー(ひまー)
もう、しょうがないなー(ねこじゃらしフリフリ)
にゃー(おなかすいた)
モモにカリカリをあげる
にゃー(マタタビほしい!)
タマにマタタビをあげる

Java の Observer クラスでは、呼び出し元のSubject クラス(Java内ではObservable クラスという名前)と、Object 全般を引数で受け取れるようになっています。

#おわりに
Push型とPull型のどちらを用いるかは、私もまだはっきりとしたことは言えません(勉強中)。ぜひ参考資料をご覧になってください。

前回はたくさんの「いいね」をいただけてとても嬉しかったです。ありがとうございます。普段見ているqiitaランキングに前回の記事が載っていて、かなり驚きました(思わずスクショを撮りました)。この記事も皆さまのお役に立てば幸いです。

#参考資料
[1][https://qiita.com/varmil/items/8cd8fe9da510e31d940a]
[2][http://d.hatena.ne.jp/backpaper0/20111111/1321012118]
[3][http://apsec99-www.se.cs.titech.ac.jp/local/material/design-patterns/resources/19.html]

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?