今回は込み入った MVC グループを生成する方法を見ていきます。 もう少し具体的には、アクションを View スクリプトから分離する方法を見ていきます(一覧)。 今回で「マルチ MVC への道」は終了(予定)。
この記事のタイトルと以下の内容だと少々誤解を招きそうなので注意しておくと、「アクションを分離する」のに必ずしも buildMVCGroup() メソッドを使う必要はありません。 buildMVCGroup() メソッドを使うサンプルを書きたかったので、この記事ではまとめて扱ってるだけです。
- 「アクションの分離」に必要なのは以下の2つです:
- Application.groovy での actions の定義
- アクションを定義したスクリプト XxxxActions.groovy
- buildMVCGroup() メソッドは MVC グループを新たに生成するメソッドで、ほとんど createMVCGroup() メソッドと同じです(違いは返り値)。
これらを踏まえて、「アクションの分離」を行う方法を見ていきましょう。
「MonolineFunction」 MVC グループのアクションを分離する
では「関数描画アプリケーション」で「MonolineFunction」 MVC グループのアクションを分離していきます。 変更を施すのは以下のソース:
- FunctionPlotter/griffon-app/conf/Application.groovy
- FunctionPlotter/griffon-app/actions/functionplotter/MonolineFunctionActions.groovy
- FunctionPlotter/griffon-app/views/functionplotter/MonolineFunctionView.groovy (actions ノードの削除)
- FunctionPlotter/griffon-app/controllers/functionPlotterController.groovy (必ずしも必要でない)
実質的には、「アクションの分離」に大事なのは上の2つだけです。
Application.groovy
まずは MVC グループのメンバとして「actions」を定義する必要があります。 これは「FunctionPlotter/griffon-app/conf」フォルダにある Application.groovy で設定します:
application { ... } mvcGroups { // MVC Group for "MonolineFunction" 'MonolineFunction' { model = 'functionplotter.MonolineFunctionModel' actions = 'functionplotter.MonolineFunctionActions' // 追加 controller = 'functionplotter.MonolineFunctionController' view = 'functionplotter.MonolineFunctionView' // これより上に actions を定義 } // MVC Group for "FunctionPlotter" 'FunctionPlotter' { ... } }
「actions」も、他のMVC グループのメンバと同じように「《ロール名》='《クラス名》'」の形で定義します。 注意が必要なのは、actions の定義は view の定義より上に追加する必要があることでしょうか。 View のインスタンス化より前に、そこで参照されるアクションがインスタンス化されていなければいけないようです(各メンバを上から順にインスタンス化するもよう)*1。
MonolineFunctionActions.groovy
次はアクションを定義するスクリプトを書きます。 ファイルは MonolineFunctionActions.groovy とし、「FunctionPlotter/griffon-app/actions/functionplotter」フォルダに配置します*2。
Actions には View に定義していたアクションを定義します。
package functionplotter action(id:'remove', name:'Remove', closure:controller.remove)
action ノードの書き方は View のものと同じです。 ただし、actions ノードは書かないことに注意*3。
MonolineFunctionView.groovy
View にはアクションを定義する必要がなくなったので、View からは actions ノードを削除します。
FunctionPlotterController.groovy
Controller のコード変更は特に必須ではありませんが、生成された MVC グループ のインスタンスをあれこれいじる場合は、createMVCGroup() の返り値(Model, View, Controller オブジェクトのリスト)よりも buildMVCGroup() の返り値(MVC グループのメンバ・オブジェクトの Map)を使った方が読むのも書くのも簡単です。
package functionplotter class FunctionPlotterController { def model def view def functionId = 'f' private List functionModels = [] as LinkedList void mvcGroupInit(Map args) { buildFunctionControl() } def addFunction = { evt = null -> def group = buildFunctionControl() this.view.with{ functionPanel.add(group.view.content) controlPanel.updateUI() } } private buildFunctionControl(String groupName = 'MonolineFunction'){ def group = buildMVCGroup(groupName, this.functionId, name:this.functionId+'(x)') this.functionModels << group.model this.functionId++ return group } ... }
以前は createFunctionControl だったメソッド名を buildFunctionControll に変更しているのは大して意味はありません。
もしコード中で、生成された MonolineFunctionActions オブジェクトにアクセスしたい場合は、他の MVC グループのメンバと同様に「actions フィールド」を定義すれば、自動でオブジェクトを注入してくれます:
class FunctionPlotterController { def model def view def actions // 生成された MonolineFunctionActions オブジェクトが自動でセットされる def someMethod(){ def removeAction = actions.remove ... } }
他にも以下のようなアクセス方法も可能です:
class FunctionPlotterController { def model def view def mvcName // この MVC グループの groupId (自動でセットされる) def someMethod(){ def removeAction = app.groups[mvcName].actions.remove ... } }
要は Application.groovy で MVC グループのメンバを定義すれば、Model, View, Controller と同じようにそのグループのメンバとして扱えるということですね。
buildMVCGroup() メソッド
最後に buildMVCGroup() メソッドの API についてまとめておきます。 といっても、以前に見た createMVCGroup() メソッドとほとんど同じです*4。 (メソッド名以外の)唯一の違いは、メソッドの返り値が MVC グループのメンバ・オブジェクトの Map だという点です。
まずは buildMVCGroup() メソッドのオーバーロード。
buildMVCGroup(String groupName) buildMVCGroup(String groupName, String groupId) buildMVCGroup(String groupName, Map params) buildMVCGroup(String groupName, String groupId, Map params)
シグニチャをまとめると下表の通り:
名前 | 型 | 必須 | 説明 |
---|---|---|---|
groupName | String | Yes | MVC グループの名前。 Application.groovy に登録されている名前。 |
groupId | String | No | グループ ID。 MVC のインスタンスにアクセスする際に指定するキーとして使う。 |
params | Map | No | MVC インスタンスの初期化に使用するパラメータ。 |
createMVCGroup() メソッドとの違いである返り値は、使い方を書いた方が分かりやすいかと:
def group = buildMVCGroup('MonolineFunction', 'f', name:'f(x)') assert group.model instanceof functionplotter.MonolineFunctionModel assert group.view instanceof functionplotter.MonolineFunctionView assert group.controller instanceof functionplotter.MonolineFunctionController assert group.actions instanceof functionplotter.MonolineFunctionActions
Map のキーとして使用されているのは、Application.groovy で指定されたキーです。
ちなみに createMVCGroup() メソッドでは、Model, View, Controller オブジェクトのリストが返されるのでした。 イメージとしてはこんな感じ:
def createMVCGroup(String groupName, String groupId = groupName, Map params = [:]){ def group = buildMVCGroup(groupName, groupId, params) return [model:group.model, view:group.view, controller:group.controller] }
実際にこういう実装になってるわけではありませんが。
これでとりあえず「マルチ MVC への道」は一段落。
- 作者: Andres Almiray,Danno Ferrin,James Shingler
- 出版社/メーカー: Manning Pubns Co
- 発売日: 2012/06/28
- メディア: ペーパーバック
- クリック: 8回
- この商品を含むブログ (19件) を見る
*1:Actions で参照される Controller のプロパティがあっても、actions を controller より先にインスタンス化して例外が発生しないのがナゾ。 スクリプトかどうかで扱いが違うようではあるけど、あまり気にするほどのものでもないかな。
*2:Application.groovy での設定を変えれば必ずしもファイル名(クラス名)を 「MonolineFunctionActions.groovy」にする必要はありません。 また、パッケージ階層にあっていれば「actions」フォルダに配置する必要もないようです。 ただし、このようにするとコードの保守が面倒になるだけなので、これについてこれ以上は触れません。
*3:拙者の環境では例外が出ました。
*4:実際、createMVCGroup() メソッドは buildMVCGroup() メソッド(で呼ばれる処理)を呼び、その返り値を変更しているだけのようです。