2005-06-02(Thu) [長年日記]
■1 車輪の再実装: 簡易DIコンテナ on Groovy
Groovyで、クラスの数が10〜15程度の規模で書かれたコードをDRY原則に従ってリファクタしていると、EoT(Ease of Testing:テスト容易性)の観点からDIコンテナが欲しくなってきた。
しかし、この規模のコードではSeasar2はおろか、PicoContainerですらヘビーウェイト。なので、id:t-wadaさんとTDD && ペアプロで作成してみた。
機能としてはAOPもXMLによる設定も無く、オブジェクトのwiringだけだが、我われの現在の用途には必要十分。インジェクション方法は、ブロック・インジェクション——Groovyだからクロージャ・インジェクションと呼ぶべきか——のみをサポート。車輪の再実装は善。って、『Linux mgazine』(2005.02)のMatzダイコンを丸パクリなので、再実装ですらないか。
利用イメージ
こんな感じ:
class Blog { input display ... } class Input {...} ... dicon = new DICon() dicon.regist("blog") {|c| new Blog(input:c.input(), display:c.display)} dicon.regist("input") {|c| new Input()} dicon.regist("display") {|c| new Display()} blog = dicon.blog # dicon.blog() でも良い blog.display...
テストコード
ガリガリ書き流したので安直だったりコピペだったりで手抜き。「Code as Documentation」になってない。
1 package com.kakutani.groovy.di 2 3 class DIConTest extends GroovyTestCase { 4 dicon 5 6 void setUp() { 7 dicon = new DICon() 8 } 9 10 void testOneCompoent() { 11 dicon.regist("myString") { "Hello World" } 12 assertEquals("Hello World", dicon.myString) 13 } 14 15 void testTwoCompoent() { 16 dicon.regist("myString") { "Hello World" } 17 dicon.regist("foo") { "Hello Foo" } 18 assertEquals("Hello Foo", dicon.foo) 19 } 20 21 void testDependency() { 22 dicon.regist("lineItem") { "ThinkPad"} 23 dicon.regist("order") {|c| 24 order = [] 25 order[0] = c.instance("lineItem") 26 order 27 } 28 29 assertEquals("ThinkPad", dicon.instance("order")[0]) 30 } 31 32 void testPropertyMissing() { 33 dicon.regist("lineItem") { "ThinkPad" } 34 dicon.regist("order") {|c| 35 order = [] 36 order[0] = c.lineItem 37 order 38 } 39 40 assertEquals("ThinkPad", dicon.order[0]) 41 } 42 43 void testMethodMissing() { 44 dicon.regist("lineItem") { "ThinkPad" } 45 dicon.regist("order") {|c| 46 order = [] 47 order[0] = c.lineItem() 48 order 49 } 50 51 assertEquals("ThinkPad", dicon.order()[0]) 52 } 53 }
プロダクトコード
Classic Syntax。JSR Syntaxは気にくわない、というのもあるけれど、MarkupとBuilderが使えないと困るので、beta10を採用。
行数は39行。所要時間は、mixiの日記のタイムスタンプから算出すると29分。getPropertyとinvokeMethodのオーバーライドでハマった(自分で翻訳していたというのに)。メソッドの仮引数の型を明示しないとオーバーライドできないのね……。
ちなみに、ブロック・インジェクションなMatzダイコンはRubyで20行。
1 package com.kakutani.groovy.di 2 3 class DICon { 4 instances 5 services 6 7 DICon() { 8 instances = [:] 9 services = [:] 10 } 11 12 DICon regist(name, closure) { 13 services[name] = closure 14 this 15 } 16 17 Object instance(name) { 18 if (instances[name] != null) return instances[name] 19 closure = services[name] 20 component = closure.call(this) 21 instances[name] = component 22 } 23 24 getProperty(String name) { 25 try { 26 return metaClass.getProperty(this, name) 27 } catch(MissingPropertyException e) { 28 return instance(name) 29 } 30 } 31 32 invokeMethod(String name, Object args) { 33 try { 34 return metaClass.invokeMethod(this, name, args) 35 } catch(MissingMethodException e) { 36 return instance(name) 37 } 38 } 39 }
さらに先に進むために
GroovyでAOPというかインターセプターというか、そーゆうのを仕込もうと思ったらどういうやり方があるのかなあ……。AspectGとか無いのかな。
■2 TodoListTutorial with Rails
id:babieさんによる翻訳。お疲れさまでしたッ!
ども。実は、このチュートリアル、原文のコード見るだけでも、十分わかりやすいんですけどねw。
http://d.hatena.ne.jp/Kazzz/20050607/p1<br>Kazzの「JとNの狭間で」<br>[Java]LightWeight DIContainer on Groovy<br> [http://www.kakutani.com/20050602.html#p01:title] Javaではなく正確にはGroovyなのだけれどDIコンテナを実装してみました、というお話し。 私が知っている中では最軽量のDIコンテナと思います。 AOPもXMLによる設定も無く、オブジェクトのwiringだけなのだそうだけど..
http://kakutani.com/20050621.html#p02<br>角谷HTML化計画<br>車輪の再実装: 簡易DIコンテナ on Groovy with AOP<br>今度はGroovyダイコンに簡易AOP機能を実装してみた。コンテナ部分のコードは45行増えて84行に。