質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

JavaFXにおけるObserverパターンの実装方法

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 751

hilty8

score 13

デザインパターンを勉強しています。
java.util.observer, java.util.observable を用いたObserverパターンを実装してみたのですが、期待通りに動いてくれません。

期待した動作

Subject = Boardクラス
Observer = MrObserverクラス
Boardクラスにあるボタンをクリックすると、MrObserverがそれを感知し、MrObserverが所有するRectangleの大きさと色が変わる、という動作を期待しています。

考えていること

Observerパターンは、以下の条件を満たせば機能すると考えています。抜け、漏れ、誤解などありましたら教えてくださると助かります。
1、Observer「MrObserver」は、implements Observer を実装
2、Subject「Board」は、Observableを継承
3、addObserverメソッドにより、Subjectは、Observerを把握
4、Subjectが何処かでnotifyObservers()を実行する
5、Observerのupdate()に処理してほしい内容を書く

Observerパターンについて、notifyObservers(), update()メソッドについて、以下の様に理解していますが、
その具体的なやり方がわかりません。
・SubjectがnotifyObservers()を実行すると、観察者側のupdate()メソッドが実行される
・notifyObservers()、update()のメソッドには引数を指定でき、そこでデータのやり取りが出来る

聞きたいこと

期待通りの結果にならない理由について、指定していただきたいと考えています。
宜しくお願い致します。

ソースコード

public class JavaFX12 extends Application{

    @Override
    public void start(Stage primaryStage) {
        Board board = new Board();
        MrObserver observer = new MrObserver();
        board.addObserver(observer);

        GridPane grid = board.grid;
        GridPane grid2 = observer.grid;
        VBox vbox = new VBox();
        vbox.getChildren().addAll(grid,grid2);

        StackPane root = new StackPane();
        root.getChildren().add(vbox);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
public class Board extends Observable{
    GridPane grid;
    public Board(){
        grid = new GridPane();

        Button btn = new Button();
        btn.setOnAction((ActionEvent event) -> {
            notifyObservers("string");
        });
        grid.add(btn, 0, 1);
    }
}
public class MrObserver implements Observer{
    GridPane grid;
    Rectangle rect;
    public MrObserver(){
        grid = new GridPane();
        rect = new Rectangle(50,50);
        rect.setFill(Color.RED);
        grid.add(rect, 0, 0);
    }
    @Override
    public void update(Observable o, Object arg) {
        rect = new Rectangle(100,100);
        rect.setFill(Color.BLUE);
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

通知の仕組み

java.util.Observableが自分の変化を監視者へ通知する際には「変わった」とマークしないと通知されないみたいです。

notifyObservers("string");
だけでは何もおこらずその前に
setChanged();
を呼び出す必要があるのですね。

こうなっている理由はObservableのデザインパターンとは多分直接関係なくて「複雑な状態を持つような一般的なObservableを設計する際に変化をどうやって手際よく通知するか」に関することだと思います。

Observableの処理の中で

あっちの処理やって
こっちの処理やって
そっちの処理やって
最後に「変化があれば通知する」

というような作りが便利ということなのでしょう。あっちこっちの処理のどこかで「状態変わった」ということが認識できたらsetChanged()とやっておくわけです。そうして処理の元締めへ戻った時「もしどこかで変化が起きていたら通知」とやるわけですね。内部状態の一つ一つが変化するごとに通知していたのでは効率的な問題、あるいは「状態変化が中途半端な状態で監視者がObservableの状態を調べ始めること」が都合が悪いためにこうなっていると思います。


ちなみにMrObserver#updateはせっかく呼び出されても期待する画面の変更は起きません。なぜなら「新たにRectangleを生成しているだけ」であってそれをJavaFXのシーングラフへ追加していないからです。

考え方を変えてください。既にシーングラフにRectangleのインスタンスがくっついている状態でupdateが呼ばれるのですからそのRectangleインスタンスの状態を変更すればよいのです。

rect.setWidth(100);
rect.setHeight(100);
rect.setFill(Color.BLUE);

「え・・・WidthやHeightを変えるだけで画面が変わるの?」と思われるかも知れません。JavaFXのNode(画面上に表示されている全てのもの)が持つwidth, height, fillといった変更可能プロパティーはjavafx.beans.Observableという監視可能なオブジェクトでして、その値を変更するとそれが自動的に画面に反映される仕組みになっているのです。これはJavaFXに限ったことではなく大抵のGUIシステムでそんな感じだと思います。Androidしかり、Unityしかり、Windows Formアプリしかりです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/13 16:39

    あ、javafx.beansパッケージの中身のクラス群はクラスやインターフェースが錯綜していて分かりにくいと思います。その点、最初からリファレンス首っ引きで理解しようとすると混乱すると思います。まずはPropertyを中心にみていくとよいでしょう。一番よく使うものです。その後でBinding(複数のObservableなものに依存して値が自動的に決まる仕組み)について知る際に関係するクラスから順に知っていけばよいと思います。

    キャンセル

  • 2018/03/13 17:19

    具体的に教えてくださって有り難うございます。
    メソッドの一覧を概観する→イメージを掴む→わかりにくかったらサンプルを探す ですね。早速やってみます。

    javafx.beans.property のことですよね。読んでみます。
    回答有り難うございました!

    キャンセル

  • 2018/03/13 18:04

    > javafx.beans.property
    おっしゃるとおりです。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる