株式会社ユーザベースの技術チームブログです。Wicketのイベントシステム徹底活用(実用サンプルもあるよ)TweetSPEEDAの開発してます矢野です。社外から参画してます。SPEEDAを書いてないときは、最近はClojureを書いています。Wicketを使っています。5から導入されたイベントシステムも、最近はかなり活用しています。イベントシステムについてはドキュメントも少なく活用方法がよくわからない、という話もあるようなので、活用方法を紹介します。手始めにWicketのあらゆるコンポーネント(つまり、Componentのサブクラス)は、イベントの送信者と受信者になることができます。payload)どのコンポーネントに対してsend()を呼び出すかは、実は重要ではありません。重要なのは、引数のsinkです。イベントは、このsinkに対して送信されます。payloadが、送信されるオブジェクトです。「イベント」と呼んで良いかと思いますが、実際にはなんでも、どんなオブジェクトでも送信できます。イベントを受け取ったsinkを起点にして、typeで指定した方法で、イベントがコンポーネントツリーに伝播します。詳しい解説はBroadcastクラスのJavadocに書かれているのですが、英語が苦手でも、ちゃんと数値でどの順序にイベントが伝播するか書かれているので、わかりやすいと思います。Broadcast一番多く使うのは、コンポーネントをsinkとしてイベントを送ると、自分自身と、そのすべての子コンポーネントに、幅優先でイベントを伝播させるBREADTHでしょう。いうか、ほとんどこれしか使わないように思います。特別に深さ優先が必要だったり(DEPTH)、そもそも伝播して欲しくなかったり(EXACT)するシーンだけ変えるくらい。イベントが伝播する途中にいるコンポーネントはすべて、伝播していくイベントを補足できます。オーバーライドします。event)IEvent#getPayload()を使って、sendに渡したpayloadオブジェクトを取得できます。あとは、このpayloadが、処理したいオブジェクトかどうかチェックして、そうであれば処理すればいいし、そうでなければ単に捨てればいいのです。いずれにせよ、イベントは次のコンポーネントに伝播します。問題ところが、このonEventが実にダサい。payloadオブジェクトの種類によって地道にチェックするしかないので、結局、次のようなコードになってしまいます。}}instanceOfでクラスチェックして、キャストして...ダサさのオンパレードで大変です。Wicketのデフォルトのイベント補足システムは、このonEventしかありません!ところで、イベントの送信側も受信側も自分で書いたプログラムであれば、他からイベントが来ることもないし、どのイベントpayloadかも把握できているわけですから、チェックせずにすぐにキャストしても良さそうに思えます。この戦術はうまくいきません。WicketのイベントシステムはWicketフレームワーク内部でも使われていて、各コンポーネントは、フレームワークが送信するイベントを受け取っています。それらと区別するためにも、必ずpayloadの種類をチェックする必要があります。書けばよいのださすがにいろんなところに、こんなinstanceofとか書いてられません。Wicketにはイベント補足システムを拡張する仕組みがあります。IEventDispatcherというインタフェースがその役目を担います。このインタフェースには、次のようなメソッドが一つだけ定義されています。component)次のように、IEventDispatcherを実装したインスタンスをApplicationのFrameworkSettingsに追加することで、イベントがコンポーネントに届いた時に、どのようにして、どんなメソッドを呼び出すのかを細く制御できます。MyGreatEventDispatcher());SPEEDAでは、この機能を利用して、次のようなイベント補足メソッドをサポートしています。アノテーションを指定することで、どんな名前のメソッドであれイベント補足メソッドになれるイベント補足メソッドは一つのクラスにいくつでも書くことができるイベント補足メソッドはprivateメソッドでも良いイベントディスパッチャは、イベント補足メソッドの引数の型を見て、送信されたpayloadの型が引数と合致するときに、そのメソッドを呼び出すイベントディスパッチャは、どのクラスがどのようなイベント補足メソッドを持っているか、記憶するこの独自のIEventDispatcher実装によって、SPEEDAでは、次のようにイベント補足メソッドを気軽に設定できています。target.appendJavaScript("showOverlay()");}@EventHandlerアノテーションをつけることで、イベント補足メソッドを作ることができます。上記のメソッドは、イベントpayloadがCriteriaChangedだったときに呼び出されます。ダサいinstanceofやキャストとはおさらばです。そのようなチェックはイベントディスパッチャが肩代わりして、すでに解決済みキャスト済みのオブジェクトが引数に渡されるのです。Licenseでもともと公開されていました。今回、新たにgistに公開したので、欲しい方はご自由に活用ください。ファイルは二つだけです。AnnotationEventDispatcher.javaEventHandler.javaコンポーネントの疎結合とクラス定義のコツWicketはもともと、HTTPの上でありながら、仮想的なオブジェクト指向環境を作り出すことで、デスクトップアプリケーションのようにウェブアプリケーションを開発できるのがウリです。独特のコンポーネントのパッケージ技術により、コンポーネントはCSSやJavaScriptとともに「ひとまとまりとして」配布できました。いった、頭にonをつけたabstractメソッドを定義し、ユーザにオーバーライドしてもらうことで、ユーザに制御を移していました。ここだけは他のクラスと密結合しているのです。5によってイベントシステムが導入されて以降、実は、コンポーネントは「クラス」「HTML」「CSS」「Javascript」に加え「イベントクラス」とパッケージ化して配布することができるようになりました!例えばダイアログを閉じた時、ユーザが項目を選んだ時などなど、そのための(オーバーライド可能)メソッドを書く代わりに、専用のpayloadを送信すればいいのです。何をしたらどのようなpayloadが送信されるかをクラスの冒頭のJavadocコメント部に書いておけば、コンポーネントは外部と独立して存在することができます。コンポーネント利用側は、イベントハンドラを書くことで、コンポーネントの更新を受け取ることができます。このような関係を実現するためには、ちょっとしたコツがあります。ノウハウあるいは定石のようなものです。コンポーネントは外部からsinkを指定できるべきコンポーネント内部で何か起こりイベントpayloadを送信するとき、どのsinkに送信するかが問題になります。これは、コンポーネント利用側が指定できるのが理想的です。コンストラクタ引数としてsinkを受け取れるのがよいと思いますが、setterメソッドでもよいでしょう。sinkが指定されてない場合はページをsinkとするsinkを指定しないのもユーザの自由です。send()の呼び出しを行うにはsinkが必要です。この場合、コンポーネントの所属するページ(getPage()で取得できます)に対して、Broadcast.BREADTHでpayloadを送信するのが良いでしょう。すれば、ページ上の全コンポーネントに対してpayloadが伝播します。sinkを指定すれば伝播の範囲が狭まるので、ページ全体に伝播させるよりパフォーマンス的に有利です。どちらを選ぶかはユーザに選ばせるべきです。コンポーネントがどのようなpayloadをいつ送信するかはドキュメント化すべきpayloadの種類と送信タイミングは、各メソッドに書くより、クラスのJavadocコメント部にまとめておく方が、見やすいですし更新も楽です。これらのプラクティスに沿えば、コンポーネントを疎結合にすることができます。onClickのようなメソッドを定義するのではなく、イベントpayloadの送信を行うことの利点は、受け取る側は「どこでも受け取れる」ということです。ページ上の、4段くらい重なったところにあるパネルにあるボタンを押すと、ページのすぐ上に乗っているコンポーネントが合わせて変化する、という場合、onXXXXといったabstractメソッドでは、どうやってページのすぐ上にあるコンポーネントまで伝えるかわかりません。イベントなら簡単です。ページにイベントハンドラとなるメソッドを書けば済むのですから。このようにイベントハンドラを多用すると、ある操作をした時にどこのメソッドが呼び出されるのかわかりにくくなる欠点があります。その面でも、この記事で紹介したAnnotationEventDispatcherを使うのが有利です。引数となるクラスは各イベント独自のものですから、あとは、IDEでそのイベントを使っているメソッドを探せばいいのです。Wicketのイベントシステムは日本語の情報がほぼないので、もしかしたら活用してない人が多いかもしれません。5以上を使っているならば、この仕組みを使わない手はありません。Wicketのイベントシステム徹底活用(実用サンプルもあるよ)Tweet関連記事2019-12-16ElixirライブラリのFlowを使ってAPIを優しく呼び出すこんにちは。SPEEDA開発チームの上村です。多国通貨」をハンズオンしたら快適で楽しかった今日は。SPEEDAの開発をやっている濱口です。今回はJavaを2年くらい...2019-10-07Ringアプリケーションで例外をいい感じにハンドリングする方法(Ductでの解説も含む)こんにちは!こんにちは!SPEEDA開発チームのあやぴーです。»プロフィール『経済情報で、世界を変える』をミッションに、企業・業界情報プラットフォーム「SPEEDA」やソーシャル経済ニュース「NewsPicks」、ABMを強力にサポートする「FORCAS」、スタートアップの情報収集を圧倒的効率化「entrepedia」などを提供する株式会社ユーザベースの技術チームブログです。多国通貨」をハンズオンしたら快適で楽しかったGo言語でオブジェクト指向プログラミングの基本(型編)月別アーカイブ検索@Uzabase_Techさんのツイートスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。