Kotlinでデザインパターン Template Method編

# はじめに

本記事は[Java言語で学ぶデザインパターン入門](http://www.hyuki.com/dp/)を参考にしながら
JavaではなくKotlinで実装してみようというものです。

![Java言語で学ぶデザインパターン入門](http://www.hyuki.com/images/dp1.png)

# 調べてみる

Template Methodについて調べてみます。
[Wikpedia](https://ja.wikipedia.org/wiki/Template_Method_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)ではTemplate Methodは次のように説明されています。

```
Template Method パターンの目的は、ある処理のおおまかなアルゴリズムをあらかじめ決めておいて、
そのアルゴリズムの具体的な設計をサブクラスに任せることである。
そのため、システムのフレームワークを構築するための手段としてよく活用される
```

毎回思うのですがWikipediaはあぁそうなんだ〜で終わる説明ですね。
Template Methodの理解を深めるためには実装したほうが早そうですので実装していきます。

# 実装してみる

次の順序で**ファイルにPersonを出力する処理**をアルゴリズムとします。
このアルゴリズムをTemplate Methodパターンを用いて実装したいと思います。

処理1. ファイルをオープンする
処理2. ファイルにPersonを書き込む
処理3. ファイルをクローズする

## Person

Personについてですが次のように定義しました。
特にデータにはこだわりありませんのでFirstNameとLastNameを持つ単純なデータクラスとしました。

```kotlin
data class Person(val firstName : String, val lastName : String)
```

## AbstractWriter

PersonAbstractWriterという抽象クラスのwriteメソッドにアルゴリズムを定義します。
処理1はOpen、処理2はprocess、処理3はcloseというメソッドで処理をします。
この3つのメソッドはabstractとなっていますのでサブクラスで具体的な処理を定義します。

このようにTemplate Methodパターンではまず、
ある処理の大まかなアルゴリズムを定義した抽象クラスを作成します。

```kotlin
abstract class AbstractWriter() {
    abstract fun open()
    abstract fun process(data : Person)
    abstract fun close()

    fun write(data : List<Person>)
    {
        // 処理1. ファイルをオープンする
        open()

      　// 処理2. ファイルにデータを書き込む
        data.forEach {
            process(it)
        }

        // 処理3. ファイルをクローズする
        close()
    }
}
```

## ConcreteWriter

次に抽象クラスのAbstractWriterを継承したサブクラスを定義し、
abstractで宣言していたメソッドをOverrideし具体的にどのように出力するか処理を実装していきます。
今回はPersonをCSV形式またはテキスト形式で書き込めるように処理を実装していきたいと思います。

### CsvWriter

CSV形式での出力では次のようにヘッダーを出力したいです。
なのでファイルをオープンするときにヘッダーを書き込んでおきます。
あとはCSV形式ですのでカンマ区切りで各プロパティを書き込むようにします。

```
lastName,firstName
姫路,太郎
榊,真喜子
喜々津,優輔
```

```kotlin
class CsvWriter(name : String) : AbstractWriter()
{
    private val name : String = name
    private val header : String = "lastName,firstName\n"
    private var file : FileWriter? = null

    override fun open() {
        file = FileWriter(name)
        file?.write(header)
    }

    override fun process(data: Person) {
        val csv = "${data.lastName},${data.firstName}\n"
        file?.write(csv)
    }

    override fun close() {
        file?.close()
    }
}
```

### TextWriter

テキスト形式ですのでPersonをそのまま書き込むだけなのですが、
次のように名簿のようにしたいと思いましたのであわせて番号も書き込むことにしました。

```
1 姫路 太郎
2 榊 真喜子
3 喜々津 優輔
```

```kotlin
class TextWriter(name : String) : AbstractWriter()
{
    private val name : String = name
    private var count : Int = 0
    private var file : FileWriter? = null

    override fun open() {
        file = FileWriter(name)
    }

    override fun process(data: Person) {
        count++

        var text = "${this.count} ${data.lastName} ${data.firstName}\n"
        file?.write(text)
    }

    override fun close() {
        file?.close()
        count = 0
    }
}
```

## Main

サブクラスの定義が終わりましたのでMainから呼び出します。

```kotlin
fun main(args : Array<String>) {

    val persons = listOf<Person>(Person("太郎", "姫路"), Person("真喜子", "榊"), Person("優輔", "喜々津"))
    val csvWriter = CsvWriter( "persons.csv")
    val textWriter = TextWriter("persons.txt")

    csvWriter.write(persons)
    textWriter.write(persons)
}
```

Mainを実行しますと次のようなファイルが次の内容で出力されます。

```
lastName,firstName
姫路,太郎
榊,真喜子
喜々津,優輔
```

```
1 姫路 太郎
2 榊 真喜子
3 喜々津 優輔
```

# おわりに

結論としてTemplate Methodパターンでやりたいことは処理の共通化です。

1. インタフェースの共通化
2. 処理順序の共通化

特に2の処理順序の共通化がTemplate Methodパターンの利点だと思います。
今回の例ですとオープンはいつクローズはいつと順序が決められているため、
サブクラスごとにオープンやクローズのタイミングが違うとかは絶対に起きなくなります。

このようにプログラマによって指針が違うような事柄が含まれる場合は、
Template Methodパターンを使って処理を共通化しておくとサブクラスがより管理しやすくなると思います。