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


はじめに

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

まずは軽く自己紹介から。
私は独立系SIerに18卒新卒社員として入社し、7月からAndroid開発案件にアサインされたひよっ子エンジニアです。
元々情報系出身だったこともあり、やりたいことをコードに落とし込む能力はそれなりかな?といった所

が、中規模システムに対して機能追加や変更を行えても、書いたコードが論理的に正しいのかが不安であったり、そもそもこのシステムは何故、どのような狙いがあってこの設計になっているのだろう。といったレベルの理解が足りないことに気づきました。

機能追加や仕様変更に強いシステムを作れるようになるため、増補改訂版Java言語で学ぶデザインパターン入門を購入して一旦デザインパターンをちゃんと勉強しようという試みを行っています。

ただ、そのままJavaのコードを書き写すのは味気無いな・・・

そうだ、Kotlinで書こう

興味がありつつも手を出せないでいたKotlinに手を出すいい機会だと思ったため、Kotlinベースで書いていきます。
Kotlinの文法は30分で覚えるKotlin文法を参考にしております。

GoogleがAndroidの開発言語として正式に認めたため、今後はJavaに代わりKotlinでの開発案件が増えていくのではないでしょうか。率直に言うとKotlin案件やりたい。
(Kotlin案件参考:Android開発を受注したからKotlinをガッツリ使ってみたら最高だった

また、デザインパターンを書いていくにあたり具体的なイメージがあると良いのでは?と考え、ポケモンに関するデータを扱うアプリを開発している設定としました。

【第1章 Iterator】

Iteratorパターンとは、何かがたくさん集まっているときに、それを順番に指し示してしていき、全体をスキャンしていく処理を行うためのものです。
Iteratorパターンを適用するメリットとしては以下のようになります。

  • 実装と切り離すため、実装に依存しない数え上げを行うことが出来る
  • 「逆順で」、「あいうえお順で」などの仕様変更に対応しやすい

実装

さっそくIteratorパターンを使ったプログラムを書いていきます。ここで作るプログラムはポケモン図鑑(Pokedex)にポケモン(Pokemon)を登録し、そのポケモンを順番に表示するというものです。

Aggregate.kt
interface Aggregate {
    fun iterator() : Iterator
}
Iterator.kt
interface Iterator {
    fun hasNext() : Boolean
    fun next() : Any
}
Pokemon.kt
data class Pokemon(val name: String, val type1: String, val type2: String)
Pokedex.kt
class Pokedex(): Aggregate {
    override fun iterator(): Iterator {
        return PokedexIterator(this)
    }

    private var pokemons = ArrayList<Pokemon>()

    fun add(pokemon: Pokemon) {
        this.pokemons.add(pokemon)
    }

    fun getPokemonAt(index: Int): Pokemon? {
        return pokemons[index]
    }

    fun getCount(): Int {
        return this.pokemons.count()
    }
}
PokedexIterator.kt
class PokedexIterator(private val pokedex: Pokedex): Iterator {
    private var index: Int = 0

    override fun next(): Any {
        val pokemon: Pokemon? = pokedex.getPokemonAt(index)
        index++
        return pokemon ?: Pokemon("けつばん", "ノーマル", "無")
    }

    override fun hasNext(): Boolean {
        return pokedex.getCount() > index
    }
}
MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val pokedex = Pokedex()
        pokedex.add(Pokemon("フシギダネ", "くさ", "どく"))
        pokedex.add(Pokemon("ヒトカゲ", "ほのお", "無"))
        pokedex.add(Pokemon("ゼニガメ", "みず", "無"))
        pokedex.add(Pokemon("ピカチュウ", "でんき", "無"))

        val pokemonSelect: Iterator = pokedex.iterator()
        while (pokemonSelect.hasNext()) {
            val pokemon: Pokemon = pokemonSelect.next() as Pokemon
            println(pokemon.toString())
        }
    }
}
出力結果
Pokemon(name=フシギダネ, type1=くさ, type2=どく)
Pokemon(name=ヒトカゲ, type1=ほのお, type2=無)
Pokemon(name=ゼニガメ, type1=みず, type2=無)
Pokemon(name=ピカチュウ, type1=でんき, type2=無)

イメージ通りの動作でいい感じですね。
Kotlin的にはポケモンデータを保持するクラスPokemon.ktが1行なのが見所だと思います。

【Kotlin文法】データクラス

何もしないけどとりあえずデータだけ保持したいクラスを作りたい。そういうのには data と付けましょう。

Pokemon.kt
data class Pokemon(val name: String, val type1: String, val type2: String)

これだけでコンパイラが勝手に以下のものを作ってくれるそうです。

  • equals()/hashCode()ペア
  • "Pokemon(name=ピカチュウ, type1=でんき, type2=無)"って表示するtoString()
  • 宣言順で内容を取り出すcomponentN()関数
  • copy()

今回はPokemon.ktで使用しました。
コンストラクタもゲッターも記述不要なんですね、便利~~~
参考文献:Kotlin文法 - データクラス, ジェネリクス