Kotlinとポケモンで学ぶデザインパターン【第3章 TemplateMethod 】


はじめに

  1. Kotlinとポケモンで学ぶデザインパターン【第1章 Iterator】
  2. Kotlinとポケモンで学ぶデザインパターン【第2章 Adapter】
  3. Kotlinとポケモンで学ぶデザインパターン【第3章 TemplateMethod 】

第三回となります。今回はTemplateMethodパターンを書いていきます。

【第3章 TemplateMethod 】

TemplateMethodパターンは、文字通りテンプレートの機能を持つパターンです。
大まかな流れとしては、スーパークラスで処理の枠組みを定め、サブクラスでその具体的内容を定めることになります。
TemplateMethodパターンを適用するメリットは以下のようになります。

  • 一連の処理の手続きを関数にまとめることが出来るので、関数の抽象度が高まる
  • 関数を継承によって差し替えるだけでいいので、拡張性が高まる

実装

今回はTemplateMethodパターンを適用してモンスターボールを投げてポケモンをゲットする流れをまとめます。
まず、ポケモンと遭遇し、ボールを投げて、結果を表示する。という処理を抽象化した抽象クラスを作ります。
AbstractCaptureクラスでは、encount, ballThrow, resultメソッドが実体のない抽象クラスとなり、抽象メソッドを使っているcaptureがテンプレートメソッドとなります。

AbstractCapture.kt
abstract class AbstractCapture {
    abstract fun encount(pokemon: Pokemon)
    abstract fun ballThrow()
    abstract fun result(pokemon: Pokemon)

    fun capture(pokemon: Pokemon) {
        encount(pokemon)
        ballThrow()
        result(pokemon)
    }
}

ポケモンを表すデータクラス

Pokemon.kt
data class Pokemon(val name: String)

野生のポケモンを捕まえる具象クラス

WildCapture.kt
class WildCapture: AbstractCapture() {
    override fun encount(pokemon: Pokemon){
        println("あ! やせいの\n${pokemon.name}が とびだしてきた!")
    }

    override fun ballThrow(){
        println("レッドは モンスターボールを なげた!")
    }

    override fun result(pokemon: Pokemon) {
        val num = Random().nextInt(2)
        println(if(num == 0) "やったー!\n${pokemon.name}を つかまえたぞ!" else "ああ!\nつかまえたと おもったのに!")
    }
}

人のポケモンを捕まえる具象クラス

TrainerCapture.kt
class TrainerCapture: AbstractCapture() {
    override fun encount(pokemon: Pokemon){
        println("あいては\n${pokemon.name}を くりだした!")
    }

    override fun ballThrow(){
        println("レッドは マスターボールを なげた!")
    }

    override fun result(pokemon: Pokemon) {
        println("トレーナーに ボールを はじかれた!")
        println("ひとの ものを とったら どろぼう!")
    }
}

抽象クラス型の変数に具象クラスのインスタンスを代入し、テンプレートメソッドを呼び出します。

MainActivity.kt
val pikachu = Pokemon("ピカチュウ")

val wild: AbstractCapture = WildCapture()
wild.capture(pikachu)

val trainer: AbstractCapture = TrainerCapture()
trainer.capture(pikachu)
実行結果
あ! やせいの 
ピカチュウが とびだしてきた!
レッドは モンスターボールを なげた!
やったー!
ピカチュウを つかまえたぞ!

あいては
ピカチュウを くりだした!
レッドは マスターボールを なげた!
トレーナーに ボールを はじかれた!
ひとの ものを とったら どろぼう!

1回しか見たことないはずなのに記憶にずっと残ってます。

【Kotlin文法】式展開

Kotlinは文字列中に$で変数が、${}で式が書けます。

Kotlin
fun encount(pokemon: Pokemon){
    println("あ! やせいの\n${pokemon.name}が とびだしてきた!")
}

Javaならばこう

Java
void encount(Pokemon pokemon){
    System.out.println("あ! やせいの\n" + pokemon.name + "が とびだしてきた!")
}

変数使ってSQLをベタ書きする時とかに式展開できる言語だと凄い楽ですね。
ちなみにRubyの式展開は#{}です。