はじめに
Kotlin には by
を使うことで委譲を簡単に実装することができます。
今回はこの仕組みについて理解して内容をまとめたいと思います。
委譲とは?
そもそも委譲についてなのですが Wikipedia では次のように定義されています。
つまり委譲とはあるオブジェクトの操作を一部他のオブジェクトに代替させる手法
です。
委譲 (英: delegation) とはオブジェクト指向プログラミングにおいて、
あるオブジェクトの操作を一部他のオブジェクトに代替させる手法のこと。
次のコードは Windows
の操作を Rectangle
に委譲している例になります。
Window
の name
や area()
で Rectangle
の name
area()
を呼び出し、
Window
が実装すべき操作を Rectangle
にやらせています。
このような時 Window
の 操作を Rectangle
に委譲していると言われます。
interface ClosedShape {
val name : String
fun area(): Int
}
class Rectangle(override val name : String, val width: Int, val height: Int) : ClosedShape {
override fun area() = width * height
}
class Window(private val bounds: ClosedShape) : ClosedShape {
// Delegation
override val name = bounds.name
override fun area() = bounds.area()
}
fun main(args: Array<String>) {
val rect = Rectangle("rectanble", 10, 10)
val window = Window(rect)
println(window.name)
println(window.area())
}
委譲するのは手間がかかる
委譲するためにあるオブジェクトから他のオブジェクト
を呼び出す必要があります。
そのため委譲するメソッドが増えると、他のオブジェクトを呼び出す処理も増えていくことになります。
呼び出し処理が増えていくと、コードの見通しも悪くなっていく問題が発生します。
また呼び出し処理を手作業で記述していくのは面倒で手間がかかります。
interface ClosedShape {
val name : String
fun area(): Int
fun new1()
fun new2()
fun new3()
}
class Rectangle(override val name : String, val width: Int, val height: Int) : ClosedShape {
override fun area() = width * height
override fun new1() { println("call new1") }
override fun new2() { println("call new2") }
override fun new3() { println("call new3") }
}
class Window(private val bounds: ClosedShape) : ClosedShape {
// Delegation
override val name = bounds.name
override fun area() = bounds.area()
override fun new1() { bounds.new1() }
override fun new2() { bounds.new2() }
override fun new3() { bounds.new3() }
}
fun main(args: Array<String>) {
val rect = Rectangle("rectanble", 10, 10)
val window = Window(rect)
println(window.name)
println(window.area())
window.new1()
window.new2()
window.new3()
}
----- OUTPUT -----
rectanble
100
call new1
call new2
call new3
by
を使って簡単に操作を委譲しよう
他のオブジェクトへの委譲を手作業やるとものすごく手間がかかります。
なので Kotlin では by
を利用することで操作を他のオブジェクトに委譲できるようになっています。
やり方は簡単で Window
の ClosedShape
に by <インスタンス名称>
を追加するだけです。
するとWindow
の ClosedShape
の操作を by
で指定したインスタンス
に全て委譲できます。
interface ClosedShape {
val name : String
fun area(): Int
fun new1()
fun new2()
fun new3()
}
class Rectangle(override val name : String, val width: Int, val height: Int) : ClosedShape {
override fun area() = width * height
override fun new1() { println("call new1") }
override fun new2() { println("call new2") }
override fun new3() { println("call new3") }
}
class Window(private val bounds: ClosedShape) : ClosedShape by bounds
fun main(args: Array<String>) {
val rect = Rectangle("rectanble", 10, 10)
val window = Window(rect)
println(window.name)
println(window.area())
window.new1()
window.new2()
window.new3()
}
----- OUTPUT -----
rectanble
100
call new1
call new2
call new3
というように by
を利用すると インタフェースで実装すべき操作
を他のオブジェクト
へ簡単に委譲できます。
次のように 2つのインタフェースに対して処理を委譲することも可能になっているので、
多くのメソッド・プロパティを持つ複数のインタフェースの処理を委譲していときはすごく便利です。
interface ButtonController {
fun up()
fun down()
fun right()
fun left()
fun a()
fun b()
fun plus()
fun minus()
fun home()
fun one()
fun two()
}
interface GyroController {
fun xyz(x : Float, y : Float, z : FXMLLoader)
}
class WiiController(private val button : ButtonController, private val gyro: GyroController)
: ButtonController by button, GyroController by gyro
}
おわりに
本記事の学びは次の3つになると思います。
Kotlin であれば委譲する時には迷わず by
を使うのが良さそうです。
- 委譲とは
あるオブジェクトの操作を一部他のオブジェクトに代替させる手法
である - 委譲では
あるオブジェクトの操作を他のオブジェクトに代替させる
ため冗長な記述が増えてしまいがち - だが Kotlinの
by
を利用することで冗長な記述をせず、簡単に操作を委譲させることができる。