SlideShare a Scribd company logo
GroovyのTraitを使い倒す
2014/06/20
JGGUG  G*Workshop
NTTソフトウェア株式会社上原潤⼆二
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
⾃自⼰己紹介
• 上原潤⼆二(@uehaj)
• NTTソフトウェア(株)Grails推進室
• JGGUG(⽇日本Grails/Groovyユーザグループ)運営委員
• 書籍執筆:プログラミングGROOVY(技術評論論
社),Grails徹底⼊入⾨門(翔泳社)
• ブログ「Grな⽇日々」
• GroovyServ,  LispBuilder,  GVM(JVM  
written  in  Groovy)開発者
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
Trait
• 「特徴」の意
• Self,  Squeak  Smalltalk,  PHP,  Rust,  
D,Scala...などにもTraitという名称の⾔言
語機能がある。C++にはTraitsという技
法がある。それぞれでの意味は、かなり/
全然違う
• Groovy  2.3で新規(⽬目⽟玉?)機能として導⼊入
• GroovyのtraitはScalaのに⽐比較的近い
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
Groovyのtrait
•   ×多重継承が可能なクラス
• 直接インスタンス化できない
•   oo実装を持てるインターフェース
• 状態も持てる(Java8のDefault  Methodとは違う)
• 多重継承に付随する「ダイヤモンド継承」問題を解決
• インスタンス変数→変数名リネーム
• メソッド衝突→線形化順序(後述)
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
traitの定義と使⽤用
• trait T { ... }
• Traitは直接インスタンス化できない
• def x = new T() // ×
• Traitを静的に実装するクラスをインスタンス化
class C implements T {} // like interface
def y = new C() // OK
• Traitを実装するプロキシを動的に⽣生成
def z = new String() as T
def z2 = new String().withTraits(T1,T2)
assert ! (z instanceof String)
assert z instanceof T
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
traitの定義と使⽤用
• trait T { ... }
• Traitは直接インスタンス化できない
• def x = new T() // ×
• Traitを静的に実装するクラスをインスタンス化
class C implements T {} // like interface
def y = new C() // OK
• Traitを実装するプロキシを動的に⽣生成
def z = new String() as T
def z2 = new String().withTraits(T1,T2)
assert ! (z instanceof String)
assert z instanceof T
「as  Interface」でプロキシ⽣生成す
るのはGroovyにもとからある機能
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
サンプルコード
trait	
  Helloable	
  {//	
  helloを表示する能力
	
  	
  void	
  hello()	
  {
	
  	
  	
  	
  println	
  "hello	
  "+this.toString()
	
  	
  }
	
  	
  void	
  hello2()	
  {
	
  	
  	
  	
  println	
  "hello	
  "+proxyTarget.toString()
	
  	
  }
}
class	
  World	
  implements	
  Helloable	
  {
	
  	
  String	
  toString(){	
  "world"}
}
x	
  =	
  new	
  World()
x.hello()	
  	
  //	
  ==>	
  hello	
  world
y	
  =	
  "abc"	
  as	
  Helloable
y.hello()	
  	
  //	
  ==>	
  hello	
  String1_groovyProxy@5560a7b1
z	
  =	
  "def"	
  as	
  Helloable
z.hello2()	
  //	
  ==>	
  hello	
  def
Traitを静的に実装するクラ
スをインスタンス化
Traitを実装するプロキシを動的に⽣生成。インスタンスへの動的メソッド追加ぽいことが
できる。状態も持てる。ただしStringそのものではなくプロキシ経由。
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
proxyTarget
• 動的に⽣生成されたTraitを実装するプロキシのみが保持す
るプロパティ
• プロキシしている元のオブジェクトを指す
• def x = new String()
def y = x.withTraits(T1,T2)
assert x == y.proxyTarget
assert y.proxyTarget instanceof String
http://jira.codehaus.org/browse/GROOVY-6692
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
traitのsuper
• Traitのメソッド中において、superのクラスは?
• traitの継承階層は⼀一意に静的に定まるツリー
ではない。superのクラスはTrait定義時には
わからない。  
• traitsを(静的/動的)にimplementsする箇
所において、traitの指定順序により異異なり得
る線形化順序がsuperのクラスを定める
• 呼び出したいTraitが確定しているなら、不不定の
superに頼らず<Trait名>.super
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
ちょっとややこしいコード
trait	
  A	
  {	
  String	
  whoami(){"A	
  -­‐>"+super.whoami()}	
  }
trait	
  B1	
  extends	
  A	
  {	
  String	
  whoami(){"B1	
  -­‐>"+super.whoami()}	
  };
trait	
  B2	
  extends	
  A	
  {	
  String	
  whoami(){"B2	
  -­‐>"+super.whoami()}	
  };
trait	
  C1	
  extends	
  B1	
  {	
  String	
  whoami(){"C1	
  -­‐>"+super.whoami()}	
  };
trait	
  C2	
  extends	
  B1	
  {	
  String	
  whoami(){"C2	
  -­‐>"+super.whoami()}	
  };
trait	
  C3	
  extends	
  B2	
  {	
  String	
  whoami(){"C3	
  -­‐>"+super.whoami()}	
  };
trait	
  C4	
  extends	
  B2	
  {	
  String	
  whoami(){"C4	
  -­‐>"+super.whoami()}	
  };
class	
  D	
  implements	
  C4,C3,A,C2,C1	
  {	
  String	
  whoami(){"D"}	
  }
class	
  E	
  extends	
  D	
  implements	
  C1,C2,C3,C4	
  {}
m	
  =	
  new	
  E()
println	
  m.whoami()
//	
  C4	
  -­‐>B2	
  -­‐>A	
  -­‐>C3	
  -­‐>C2	
  -­‐>B1	
  -­‐>C1	
  -­‐>D
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2
<<trait>>
A
<<Class>>
D
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2
<<trait>>
A
<<Class>>
D <<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2
<<trait>>
A
<<Class>>
D <<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2
<<trait>>
A
<<Class>>
D <<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2
<<trait>>
A
①
<<Class>>
D <<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2 ②
<<trait>>
A
①
<<Class>>
D <<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2 ②
<<trait>>
A
①
<<Class>>
D
③
<<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2 ②
<<trait>>
A
①④
<<Class>>
D
③
<<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2 ②
<<trait>>
A
①⑤ ④
<<Class>>
D
③
<<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4
<<trait>>
B1
<<trait>>
B2 ②⑥
<<trait>>
A
①⑤ ④
<<Class>>
D
③
<<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4⑦
<<trait>>
B1
<<trait>>
B2 ②⑥
<<trait>>
A
①⑤ ④
<<Class>>
D
③
<<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4⑦
<<trait>>
B1
<<trait>>
B2 ②⑥
<<trait>>
A
①⑤ ④
<<Class>>
D ⑧
③
<<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
継承階層の線形化順序
(メソッド探索索したり、superを辿る順序)
extends
(implements)
extends
<<trait>>
C1
<<trait>>
C2
<<trait>>
C3
<<trait>>
C4⑦
<<trait>>
B1
<<trait>>
B2 ②⑥
<<trait>>
A
①⑤ ④
<<Class>>
D ⑧
③
<<Class>>
E
クラスDにtrait C1∼C4から機
能注入したものをEとする
class E extends D
implements C1,C2,C3,C4
Dがどんな順序でどの
traitをimplementsして
いるかは、Eからの
superリンクの形成には
無関係
Eから始めて、
右側優先、下か
ら上への深さ優
先探索
Scalaのと
は異なる
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
コード再掲
trait	
  A	
  {	
  String	
  whoami(){"A	
  -­‐>"+super.whoami()}	
  }
trait	
  B1	
  extends	
  A	
  {	
  String	
  whoami(){"B1	
  -­‐>"+super.whoami()}	
  };
trait	
  B2	
  extends	
  A	
  {	
  String	
  whoami(){"B2	
  -­‐>"+super.whoami()}	
  };
trait	
  C1	
  extends	
  B1	
  {	
  String	
  whoami(){"C1	
  -­‐>"+super.whoami()}	
  };
trait	
  C2	
  extends	
  B1	
  {	
  String	
  whoami(){"C2	
  -­‐>"+super.whoami()}	
  };
trait	
  C3	
  extends	
  B2	
  {	
  String	
  whoami(){"C3	
  -­‐>"+super.whoami()}	
  };
trait	
  C4	
  extends	
  B2	
  {	
  String	
  whoami(){"C4	
  -­‐>"+super.whoami()}	
  };
class	
  D	
  implements	
  C4,C3,A,C2,C1	
  {	
  String	
  whoami(){"D"}	
  }
class	
  E	
  extends	
  D	
  implements	
  C1,C2,C3,C4	
  {}
m	
  =	
  new	
  E()
println	
  m.whoami()
//	
  C4	
  -­‐>B2	
  -­‐>A	
  -­‐>C3	
  -­‐>C2	
  -­‐>B1	
  -­‐>C1	
  -­‐>D ここが重要
Dでimplementsするtraitsの順序や有無
はEからのsuperリンクの形成に無関係
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
線形化された継承階層(1)
class	
  E	
  extends	
  D	
  implements	
  
C1,C2,C3,C4	
  {} <<trait>>
C1 ⑦
<<trait>>
B2 ②
<<trait>>
B1 ⑥
<<trait>>
C4 ①
<<trait>>
C2 ⑤
<<trait>>
C3 ④
<<Class>>
E
<<Class>>
D ⑧
<<trait>>
A ③
trait勢はClass継承の
親との間に挿入される
(β版での
@ForceOverride指定
の動作)
• 右のように単⼀一継
承しているかのよ
うにsuperが動作
• クラスDがsuper
リンクの終端点と
して機能
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
線形化された継承階層(2)
class	
  E	
  implements	
  C1,C2,C3,C4	
  {}
<<trait>>
C1 ⑦
<<trait>>
B2 ②
<<trait>>
B1 ⑥
<<trait>>
C4 ①
<<trait>>
C2 ⑤
<<trait>>
C3 ④
<<Class>>
E
Object(?)
<<trait>>
A ③
• Dをextendsせず、trait(例例えば
C1)を終端点としても良良いが、
• 線形化順序上、C1が常に末端
になるようにプログラマが保証
する必要がある。
• C1ではsuper.whoami()が失
敗
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
Stackable  Trait
• Traitの指定順序が異異なると線形化順序が
異異なることを利利⽤用しメソッドの適⽤用順序
を変更更
• 継承階層を、あと付けで組み替える
• 組み合わせ可能なデコレータ
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
コード例例  Wikiエンジン(1)
trait	
  Bold	
  {
	
  	
  String	
  convert(String	
  s)	
  {
	
  	
  	
  	
  super.convert(s).replaceAll(/**([^*]*)**/,	
  '<b>$1</b>')
	
  	
  }
}
trait	
  Heading	
  {
	
  	
  	
  	
  String	
  convert(String	
  s)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  super.convert(s).replaceAll(/(?m)^(#+)	
  ?(.*)$/)	
  {	
  g0,	
  g1,	
  g2-­‐>
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "<h"+g1.size()+">"+g2+"</h"+g1.size()+">"
	
  	
  	
  	
  	
  	
  	
  	
  }
	
  	
  	
  	
  }
}
trait	
  UL	
  {
	
  	
  	
  	
  String	
  convert(String	
  s)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  super.convert(s).replaceAll(/(?m)(^s*[^*]*n)(s**)/)	
  {	
  g0,	
  g1,	
  g2-­‐>
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  g1+"<ul>n"+g2
	
  	
  	
  	
  	
  	
  	
  	
  }.replaceAll(/(?m)(^s**.*n)(s*[^*]*n)/)	
  {	
  g0,	
  g1,	
  g2	
  -­‐>
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  g1+"</ul>n"+g2
	
  	
  	
  	
  	
  	
  	
  	
  }.replaceAll(/(?m)^s**s*(.*)$/,	
  "<li>$1</li>")
	
  	
  	
  	
  }
}
class	
  HtmlConverter	
  {
	
  	
  	
  	
  String	
  convert(String	
  s)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  "<html>"+s+"n</html>n"
	
  	
  	
  	
  }
}
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
コード例例  Wikiエンジン(2)
class	
  WikiEngine1	
  extends	
  HtmlConverter	
  implements	
  Bold,	
  Heading,	
  UL	
  {}
class	
  WikiEngine2	
  extends	
  HtmlConverter	
  implements	
  UL,	
  Bold,	
  Heading	
  {}
println	
  new	
  WikiEngine1().convert(text)
println	
  new	
  WikiEngine2().convert(text)
println	
  new	
  HtmlConverter().withTraits(Bold,	
  Heading,	
  UL).convert(text)
println	
  new	
  HtmlConverter().withTraits(UL,	
  Bold,	
  Heading).convert(text)
##this	
  is	
  Heading1
#this	
  is	
  Heading2
This	
  is	
  sample	
  document	
  for	
  wiki	
  
engine.
If	
  you	
  wan	
  to	
  **emphasize**,	
  use	
  
**This**	
  markup.
#This	
  is	
  heading3
This	
  is	
  sample	
  document	
  for	
  wiki	
  
engine.
*	
  list1
*	
  list2
*	
  list3
This	
  is	
  end.
**hoge**
<html>
<h2>this	
  is	
  Heading1</h2>
<h1>this	
  is	
  Heading2</h1>
This	
  is	
  sample	
  document	
  for	
  wiki	
  engine.
If	
  you	
  wan	
  to	
  <b>emphasize</b>,	
  use	
  
<b>This</b>	
  markup.
<h1>This	
  is	
  heading3</h1>
This	
  is	
  sample	
  document	
  for	
  wiki	
  engine.
<ul>
<li>list1</li>
<li>list2</li>
<li>list3</li>
</ul>
This	
  is	
  end.
<b>hoge</b>
</html>
<ul>
<li>*hoge**</li>
</ul>
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
まとめ
• Groovyのtraitはmixinに近い
• ⾃自⾝身のデータ構造とメソッド群によって独⽴立立して機能実現。しか
る後にその機能が欲しいクラスに注⼊入する。
• 単⼀一継承じゃだめなんですかオブジェクトコンポジションじゃ…
• なくても死なない
• しかし、クラス階層(分類の階層、代⼊入可能性の制約)と機能実装
の階層は本来独⽴立立。機能合成の階層構造が単⼀一継承のツリーであ
る必然性はない。
• (例例)  read  from  file  +  write  to  file  =>  read  write  file
• GroovyのライブラリはJavaベースなのでTrait使ってない。
使いどころはアプリコード。DI的に使ったり?  DCI?
• Scalaでの利利⽤用が参考になるかも。
14年6月21日土曜日
Copyright (C) 2014 NTT Software Corp.
参考リンクその他
• 参考リンク
• http://beta.groovy-‐‑‒lang.org/docs/latest/html/documentation/
#_̲traits
• http://www.artima.com/scalazine/articles/
stackable_̲trait_̲pattern.html
• http://www.atmarkit.co.jp/ait/articles/1206/20/
news137_̲3.html
• サンプルコード
• http://groovyconsole.appspot.com/script/5738600293466112
• http://groovyconsole.appspot.com/script/5653164804014080
• 商標
• OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録
商標である場合があります。
14年6月21日土曜日

More Related Content

Introduce Groovy 2.3 trait

  • 2. Copyright (C) 2014 NTT Software Corp. ⾃自⼰己紹介 • 上原潤⼆二(@uehaj) • NTTソフトウェア(株)Grails推進室 • JGGUG(⽇日本Grails/Groovyユーザグループ)運営委員 • 書籍執筆:プログラミングGROOVY(技術評論論 社),Grails徹底⼊入⾨門(翔泳社) • ブログ「Grな⽇日々」 • GroovyServ,  LispBuilder,  GVM(JVM   written  in  Groovy)開発者 14年6月21日土曜日
  • 3. Copyright (C) 2014 NTT Software Corp. Trait • 「特徴」の意 • Self,  Squeak  Smalltalk,  PHP,  Rust,   D,Scala...などにもTraitという名称の⾔言 語機能がある。C++にはTraitsという技 法がある。それぞれでの意味は、かなり/ 全然違う • Groovy  2.3で新規(⽬目⽟玉?)機能として導⼊入 • GroovyのtraitはScalaのに⽐比較的近い 14年6月21日土曜日
  • 4. Copyright (C) 2014 NTT Software Corp. Groovyのtrait • ×多重継承が可能なクラス • 直接インスタンス化できない •  oo実装を持てるインターフェース • 状態も持てる(Java8のDefault  Methodとは違う) • 多重継承に付随する「ダイヤモンド継承」問題を解決 • インスタンス変数→変数名リネーム • メソッド衝突→線形化順序(後述) 14年6月21日土曜日
  • 5. Copyright (C) 2014 NTT Software Corp. traitの定義と使⽤用 • trait T { ... } • Traitは直接インスタンス化できない • def x = new T() // × • Traitを静的に実装するクラスをインスタンス化 class C implements T {} // like interface def y = new C() // OK • Traitを実装するプロキシを動的に⽣生成 def z = new String() as T def z2 = new String().withTraits(T1,T2) assert ! (z instanceof String) assert z instanceof T 14年6月21日土曜日
  • 6. Copyright (C) 2014 NTT Software Corp. traitの定義と使⽤用 • trait T { ... } • Traitは直接インスタンス化できない • def x = new T() // × • Traitを静的に実装するクラスをインスタンス化 class C implements T {} // like interface def y = new C() // OK • Traitを実装するプロキシを動的に⽣生成 def z = new String() as T def z2 = new String().withTraits(T1,T2) assert ! (z instanceof String) assert z instanceof T 「as  Interface」でプロキシ⽣生成す るのはGroovyにもとからある機能 14年6月21日土曜日
  • 7. Copyright (C) 2014 NTT Software Corp. サンプルコード trait  Helloable  {//  helloを表示する能力    void  hello()  {        println  "hello  "+this.toString()    }    void  hello2()  {        println  "hello  "+proxyTarget.toString()    } } class  World  implements  Helloable  {    String  toString(){  "world"} } x  =  new  World() x.hello()    //  ==>  hello  world y  =  "abc"  as  Helloable y.hello()    //  ==>  hello  String1_groovyProxy@5560a7b1 z  =  "def"  as  Helloable z.hello2()  //  ==>  hello  def Traitを静的に実装するクラ スをインスタンス化 Traitを実装するプロキシを動的に⽣生成。インスタンスへの動的メソッド追加ぽいことが できる。状態も持てる。ただしStringそのものではなくプロキシ経由。 14年6月21日土曜日
  • 8. Copyright (C) 2014 NTT Software Corp. proxyTarget • 動的に⽣生成されたTraitを実装するプロキシのみが保持す るプロパティ • プロキシしている元のオブジェクトを指す • def x = new String() def y = x.withTraits(T1,T2) assert x == y.proxyTarget assert y.proxyTarget instanceof String http://jira.codehaus.org/browse/GROOVY-6692 14年6月21日土曜日
  • 9. Copyright (C) 2014 NTT Software Corp. traitのsuper • Traitのメソッド中において、superのクラスは? • traitの継承階層は⼀一意に静的に定まるツリー ではない。superのクラスはTrait定義時には わからない。   • traitsを(静的/動的)にimplementsする箇 所において、traitの指定順序により異異なり得 る線形化順序がsuperのクラスを定める • 呼び出したいTraitが確定しているなら、不不定の superに頼らず<Trait名>.super 14年6月21日土曜日
  • 10. Copyright (C) 2014 NTT Software Corp. ちょっとややこしいコード trait  A  {  String  whoami(){"A  -­‐>"+super.whoami()}  } trait  B1  extends  A  {  String  whoami(){"B1  -­‐>"+super.whoami()}  }; trait  B2  extends  A  {  String  whoami(){"B2  -­‐>"+super.whoami()}  }; trait  C1  extends  B1  {  String  whoami(){"C1  -­‐>"+super.whoami()}  }; trait  C2  extends  B1  {  String  whoami(){"C2  -­‐>"+super.whoami()}  }; trait  C3  extends  B2  {  String  whoami(){"C3  -­‐>"+super.whoami()}  }; trait  C4  extends  B2  {  String  whoami(){"C4  -­‐>"+super.whoami()}  }; class  D  implements  C4,C3,A,C2,C1  {  String  whoami(){"D"}  } class  E  extends  D  implements  C1,C2,C3,C4  {} m  =  new  E() println  m.whoami() //  C4  -­‐>B2  -­‐>A  -­‐>C3  -­‐>C2  -­‐>B1  -­‐>C1  -­‐>D 14年6月21日土曜日
  • 11. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 <<trait>> A <<Class>> D 14年6月21日土曜日
  • 12. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 <<trait>> A <<Class>> D <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 14年6月21日土曜日
  • 13. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 <<trait>> A <<Class>> D <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 14年6月21日土曜日
  • 14. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 <<trait>> A <<Class>> D <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 15. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 <<trait>> A ① <<Class>> D <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 16. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 ② <<trait>> A ① <<Class>> D <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 17. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 ② <<trait>> A ① <<Class>> D ③ <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 18. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 ② <<trait>> A ①④ <<Class>> D ③ <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 19. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 ② <<trait>> A ①⑤ ④ <<Class>> D ③ <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 20. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4 <<trait>> B1 <<trait>> B2 ②⑥ <<trait>> A ①⑤ ④ <<Class>> D ③ <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 21. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4⑦ <<trait>> B1 <<trait>> B2 ②⑥ <<trait>> A ①⑤ ④ <<Class>> D ③ <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 22. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4⑦ <<trait>> B1 <<trait>> B2 ②⑥ <<trait>> A ①⑤ ④ <<Class>> D ⑧ ③ <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 23. Copyright (C) 2014 NTT Software Corp. 継承階層の線形化順序 (メソッド探索索したり、superを辿る順序) extends (implements) extends <<trait>> C1 <<trait>> C2 <<trait>> C3 <<trait>> C4⑦ <<trait>> B1 <<trait>> B2 ②⑥ <<trait>> A ①⑤ ④ <<Class>> D ⑧ ③ <<Class>> E クラスDにtrait C1∼C4から機 能注入したものをEとする class E extends D implements C1,C2,C3,C4 Dがどんな順序でどの traitをimplementsして いるかは、Eからの superリンクの形成には 無関係 Eから始めて、 右側優先、下か ら上への深さ優 先探索 Scalaのと は異なる 14年6月21日土曜日
  • 24. Copyright (C) 2014 NTT Software Corp. コード再掲 trait  A  {  String  whoami(){"A  -­‐>"+super.whoami()}  } trait  B1  extends  A  {  String  whoami(){"B1  -­‐>"+super.whoami()}  }; trait  B2  extends  A  {  String  whoami(){"B2  -­‐>"+super.whoami()}  }; trait  C1  extends  B1  {  String  whoami(){"C1  -­‐>"+super.whoami()}  }; trait  C2  extends  B1  {  String  whoami(){"C2  -­‐>"+super.whoami()}  }; trait  C3  extends  B2  {  String  whoami(){"C3  -­‐>"+super.whoami()}  }; trait  C4  extends  B2  {  String  whoami(){"C4  -­‐>"+super.whoami()}  }; class  D  implements  C4,C3,A,C2,C1  {  String  whoami(){"D"}  } class  E  extends  D  implements  C1,C2,C3,C4  {} m  =  new  E() println  m.whoami() //  C4  -­‐>B2  -­‐>A  -­‐>C3  -­‐>C2  -­‐>B1  -­‐>C1  -­‐>D ここが重要 Dでimplementsするtraitsの順序や有無 はEからのsuperリンクの形成に無関係 14年6月21日土曜日
  • 25. Copyright (C) 2014 NTT Software Corp. 線形化された継承階層(1) class  E  extends  D  implements   C1,C2,C3,C4  {} <<trait>> C1 ⑦ <<trait>> B2 ② <<trait>> B1 ⑥ <<trait>> C4 ① <<trait>> C2 ⑤ <<trait>> C3 ④ <<Class>> E <<Class>> D ⑧ <<trait>> A ③ trait勢はClass継承の 親との間に挿入される (β版での @ForceOverride指定 の動作) • 右のように単⼀一継 承しているかのよ うにsuperが動作 • クラスDがsuper リンクの終端点と して機能 14年6月21日土曜日
  • 26. Copyright (C) 2014 NTT Software Corp. 線形化された継承階層(2) class  E  implements  C1,C2,C3,C4  {} <<trait>> C1 ⑦ <<trait>> B2 ② <<trait>> B1 ⑥ <<trait>> C4 ① <<trait>> C2 ⑤ <<trait>> C3 ④ <<Class>> E Object(?) <<trait>> A ③ • Dをextendsせず、trait(例例えば C1)を終端点としても良良いが、 • 線形化順序上、C1が常に末端 になるようにプログラマが保証 する必要がある。 • C1ではsuper.whoami()が失 敗 14年6月21日土曜日
  • 27. Copyright (C) 2014 NTT Software Corp. Stackable  Trait • Traitの指定順序が異異なると線形化順序が 異異なることを利利⽤用しメソッドの適⽤用順序 を変更更 • 継承階層を、あと付けで組み替える • 組み合わせ可能なデコレータ 14年6月21日土曜日
  • 28. Copyright (C) 2014 NTT Software Corp. コード例例  Wikiエンジン(1) trait  Bold  {    String  convert(String  s)  {        super.convert(s).replaceAll(/**([^*]*)**/,  '<b>$1</b>')    } } trait  Heading  {        String  convert(String  s)  {                super.convert(s).replaceAll(/(?m)^(#+)  ?(.*)$/)  {  g0,  g1,  g2-­‐>                        "<h"+g1.size()+">"+g2+"</h"+g1.size()+">"                }        } } trait  UL  {        String  convert(String  s)  {                super.convert(s).replaceAll(/(?m)(^s*[^*]*n)(s**)/)  {  g0,  g1,  g2-­‐>                        g1+"<ul>n"+g2                }.replaceAll(/(?m)(^s**.*n)(s*[^*]*n)/)  {  g0,  g1,  g2  -­‐>                        g1+"</ul>n"+g2                }.replaceAll(/(?m)^s**s*(.*)$/,  "<li>$1</li>")        } } class  HtmlConverter  {        String  convert(String  s)  {                "<html>"+s+"n</html>n"        } } 14年6月21日土曜日
  • 29. Copyright (C) 2014 NTT Software Corp. コード例例  Wikiエンジン(2) class  WikiEngine1  extends  HtmlConverter  implements  Bold,  Heading,  UL  {} class  WikiEngine2  extends  HtmlConverter  implements  UL,  Bold,  Heading  {} println  new  WikiEngine1().convert(text) println  new  WikiEngine2().convert(text) println  new  HtmlConverter().withTraits(Bold,  Heading,  UL).convert(text) println  new  HtmlConverter().withTraits(UL,  Bold,  Heading).convert(text) ##this  is  Heading1 #this  is  Heading2 This  is  sample  document  for  wiki   engine. If  you  wan  to  **emphasize**,  use   **This**  markup. #This  is  heading3 This  is  sample  document  for  wiki   engine. *  list1 *  list2 *  list3 This  is  end. **hoge** <html> <h2>this  is  Heading1</h2> <h1>this  is  Heading2</h1> This  is  sample  document  for  wiki  engine. If  you  wan  to  <b>emphasize</b>,  use   <b>This</b>  markup. <h1>This  is  heading3</h1> This  is  sample  document  for  wiki  engine. <ul> <li>list1</li> <li>list2</li> <li>list3</li> </ul> This  is  end. <b>hoge</b> </html> <ul> <li>*hoge**</li> </ul> 14年6月21日土曜日
  • 30. Copyright (C) 2014 NTT Software Corp. まとめ • Groovyのtraitはmixinに近い • ⾃自⾝身のデータ構造とメソッド群によって独⽴立立して機能実現。しか る後にその機能が欲しいクラスに注⼊入する。 • 単⼀一継承じゃだめなんですかオブジェクトコンポジションじゃ… • なくても死なない • しかし、クラス階層(分類の階層、代⼊入可能性の制約)と機能実装 の階層は本来独⽴立立。機能合成の階層構造が単⼀一継承のツリーであ る必然性はない。 • (例例)  read  from  file  +  write  to  file  =>  read  write  file • GroovyのライブラリはJavaベースなのでTrait使ってない。 使いどころはアプリコード。DI的に使ったり?  DCI? • Scalaでの利利⽤用が参考になるかも。 14年6月21日土曜日
  • 31. Copyright (C) 2014 NTT Software Corp. 参考リンクその他 • 参考リンク • http://beta.groovy-‐‑‒lang.org/docs/latest/html/documentation/ #_̲traits • http://www.artima.com/scalazine/articles/ stackable_̲trait_̲pattern.html • http://www.atmarkit.co.jp/ait/articles/1206/20/ news137_̲3.html • サンプルコード • http://groovyconsole.appspot.com/script/5738600293466112 • http://groovyconsole.appspot.com/script/5653164804014080 • 商標 • OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録 商標である場合があります。 14年6月21日土曜日