Help us understand the problem. What is going on with this article?

[Kotlin]`by` で 委譲する仕組みを理解する

はじめに

Kotlin には by を使うことで委譲を簡単に実装することができます。
今回はこの仕組みについて理解して内容をまとめたいと思います。

委譲とは?

そもそも委譲についてなのですが Wikipedia では次のように定義されています。
つまり委譲とはあるオブジェクトの操作を一部他のオブジェクトに代替させる手法です。

委譲 (英: delegation) とはオブジェクト指向プログラミングにおいて、
あるオブジェクトの操作を一部他のオブジェクトに代替させる手法のこと。

次のコードは Windowsの操作を Rectangleに委譲している例になります。
Windownamearea()Rectanglename 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 を利用することで操作を他のオブジェクトに委譲できるようになっています。

やり方は簡単で WindowClosedShapeby <インスタンス名称> を追加するだけです。
するとWindowClosedShape の操作を 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 を利用することで冗長な記述をせず、簡単に操作を委譲させることができる。

参考文献

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした