デザインパターンをkotlinで書いてみた　Builder編

オブジェクト指向で大切になるInterfaceの考え方やオブジェクトの再利用性を学ぶために[「Java言語で学ぶデザインパターン入門」](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80&qid=1559563427&s=gateway&sr=8-1)について学び、Javaとついでにkotlinで書いてみることにしました。
今回はBuilderについて書いてみます。

##Builderとは
構造物を構築する際は、各段階を踏んで組み上げていくように、構造を持ったインスタンスを組み上げていくパターンです。
サンプルプログラムでは文章という構造物を扱い、
文章は以下の段階・構造を持っているとして、各段階を構成するメソッドを定めていきます。

>1. タイトルを一つ含む
2. 文字列をいくつか含む
3. 箇条書きの項目をいくつか含む

##Builderクラス
文章を構成するためのメソッドを定めたクラスです。

```java:Builder.java
abstract class Builder {
	public abstract void makeTitle(String title);
	public abstract void makeString(String str);
	public abstract void makeItems(String[] items);
	public abstract void close();
}
```
```kotlin:Builder.kt
abstract class Builder {
    abstract fun makeTitle(title: String)
    abstract fun makeString(str: String)
    abstract fun makeItems(items: Array<String>)
    abstract fun close()
}
```

##Directorクラス
このクラスでは文章を構築する`construct`メソッドを実装しています。
Builderクラスがコンストラクタで渡されているが、Builderは抽象クラスの為、実際には継承したサブクラスが入ります。
具体的なサブクラスの宣言がないので依存性がなくなり、
また、サブクラスの入れ替えが可能になることを__交換可能性__と呼ぶとのこと。

```java:Director.java
class Director {
	private Builder builder;
	public Director(Builder builder) {
		this.builder = builder;
	}
	public void construct() {
		builder.makeTitle("Greeting");
		builder.makeString("朝から昼にかけて");
		builder.makeItems(new String[] {
				"おはようございます。",
				"こんにちは。"
		});
		builder.makeString("夜に");
		builder.makeItems(new String[] {
				"こんばんは。",
				"おやすみなさい。",
				"さようなら。"
		});
		builder.close();
	}
}
```
```kotlin:Director.kt
class Director(builder: Builder) {
    private var b = builder
    fun construct(){
        b.makeTitle("Greeting")
        b.makeString("朝から昼かけて")
        b.makeItems(arrayOf("おはようございます。", "こんにちは。"))
        b.makeString("夜に")
        b.makeItems(arrayOf("こんばんは。", "おやすみなさい。", "さようなら。"))
        b.close()
    }
}
```

##TextBuilderクラス
プレーンテキストを使って文書を作成するクラスです。

kotlinでfor文を書いた際に、最初は`for (i: Int in 0..items.size - 1)`と書いてしまったのですが、
`for (i: Int in 0 until items.size)`のようにuntilは末数を数えないのでArrayの操作に向いているとのことです。

>参考：[KotlinでAndroid開発！　基本構文をおさえよう](https://tech.pjin.jp/blog/2017/03/01/android_with_kotlin_basicsyntax/)

kotlinだと`String Template`(文字列補間)と呼ばれる便利な書き方ができ、
`println("$a + $b = ${a + b}") // 2 + 3 = 5`のように文字列を埋め込めるとのことです。

>参考：[JavaプログラマがKotlinで便利だと感じること](https://qiita.com/koher/items/cb91dbbff9b14575f498)

```java:TextBuilder.java
class TextBuilder extends Builder{
	private StringBuffer buffer = new StringBuffer();
	public void makeTitle(String title) {
		buffer.append("================================================\n");
		buffer.append(String.format("[%s]\n", title));
		buffer.append("\n");
	}
	public void makeString(String str) {
		buffer.append(String.format("■%s\n", str));
		buffer.append("\n");
	}
	public void makeItems(String[] items) {
		for (int i = 0; i < items.length; i++) {
			buffer.append(String.format("　・%s\n", items[i]));
		}
		buffer.append("\n");
	}
	public void close() {
		buffer.append("================================================\n");
	}
	public String getResult() {
		return buffer.toString();
	}
}
```
```kotlin:TextBuilder.kt
class TextBuilder: Builder() {
    private var buffer = StringBuffer()
    override fun makeTitle(title: String) {
        buffer.append("================================================\n")
        buffer.append("[$title]\n")
        buffer.append("\n")
    }
    override fun makeString(str: String) {
        buffer.append("■$str\n")
        buffer.append("\n")
    }
    override fun makeItems(items: Array<String>) {
        for (i: Int in 0 until items.size) buffer.append("　・${items[i]}\n")
        buffer.append("\n")
    }
    override fun close() {
        buffer.append("================================================\n")
    }
    fun getResult() = buffer.toString()
}
```

##HTMLBuilderクラス
HTMLファイルを使って文書を作成するクラスです。

writerメンバをFileクラスで初期化できずに困ったのですが、`lateinit`を使用して処理が進むにつれて変更されていく値を遅延させることができるとのことです。
また、lateinitは初期化が行われる前に外部からアクセスできる困るので`private`推奨とのことです。
>参考：[Kotlinで初期化を遅延する](https://re-engines.com/2018/11/15/kotlinで初期化を遅延する/)

printWriterを取得するには`java.io.File#printWriter()`を使用すれば取得でき、ファイルが無ければ作成され、あれば上書きされます。
追記したい場合は`appendText()`を使用します。

>参考：[I/O(ファイル,ネットワーク等)](https://sites.google.com/site/memo73737/kotlin/io#TOC-printWriter)

もし、ファイルの有無によって動作を変えたい場合は`java.io.File#createNewFile():Boolean`を使用します。

>参考：[Kotlin – Create File – Examples](https://www.tutorialkart.com/kotlin/kotlin-create-file/)

```java:HTMLBuilder.java
import java.io.*;

class HTMLBuilder extends Builder{
	private String filename;
	private PrintWriter writer;
	public void makeTitle(String title) {
		filename = title + ".html";
		try {
			writer = new PrintWriter(filename);
		} catch (IOException e) {
			e.printStackTrace();
		}
		writer.println(String.format("<html><head><title>%s</title></head><body>", title));
		writer.println(String.format("<h1>%s</h1>", title));
	}
	public void makeString(String str) {
		writer.println(String.format("<p>%s</p>", str));
	}
	public void makeItems(String[] items) {
		writer.println("<ul>");
		for (int i = 0; i < items.length; i++) {
			writer.println(String.format("<li>%s</li>", items[i]));
		}
		writer.println("</ul>");
	}
	public void close() {
		writer.println("</body></html>");
		writer.close();
	}
	public String getResult() {
		return filename;
	}
}
```
```kotlin:HTMLBuilder.kt
import java.io.File

class HTMLBuilder: Builder() {
    private var filename = String()
    private lateinit var writer:File
    override fun makeTitle(title: String) {
        filename = "$title.html"
        writer = File(filename)
        writer.printWriter().use { it.println("<html><head><title>$filename</title></head><body>") }
        writer.appendText("<h1>$title</h1>\n")
    }
    override fun makeString(str: String) {
        writer.appendText("<p>$str</p>\n")
    }
    override fun makeItems(items: Array<String>) {
        writer.appendText("<ul>\n")
        for (i: Int in 0 until items.size) writer.appendText("<li>${items[i]}</li>\n")
        writer.appendText("</ul>\n")
    }
    override fun close() {
        writer.appendText("</body></html>\n")
    }
    fun getResult() = filename
}
```

##Mainクラス
```java:BuilderSample.java
public class BuilderSample {
	public static void main(String[] args) {
		if (args.length != 1) {
			usage();
			System.exit(0);
		}
		if (args[0].equals("plain")) {
			TextBuilder textbuilder = new TextBuilder();
			Director director = new Director(textbuilder);
			director.construct();
			String result = textbuilder.getResult();
			System.out.println(result);
		} else if (args[0].equals("html")) {
			HTMLBuilder htmlbuilder = new HTMLBuilder();
			Director director = new Director(htmlbuilder);
			director.construct();
			String filename = htmlbuilder.getResult();
			System.out.println(String.format("%sが作成されました。", filename));
		} else {
			usage();
			System.exit(0);
		}
	}
	public static void usage() {
		System.out.println("Usage: java Main plain プレーンテキストで文書作成");
		System.out.println("Usage: java Main html  HTMLファイルで文書作成");
	}
}
```
```kotlin:BuilderSample.kt
fun main(args: Array<String>) {
    if (args.size != 1){
        usage()
        System.exit(0)
    }
    if (args[0] == "plain"){
        val textbuilder = TextBuilder()
        val director = Director(textbuilder)
        director.construct()
        val result = textbuilder.getResult()
        println(result)
    } else if (args[0] == "html") {
        val htmlbuilder = HTMLBuilder()
        val director = Director(htmlbuilder)
        director.construct()
        val filename = htmlbuilder.getResult()
        println(filename)
    } else {
        usage()
        System.exit(0)
    }

}
fun usage() {
    println("Usage: java Main plain プレーンテキストで文書作成")
    println("Usage: java Main html  HTMLファイルで文書作成")
}
```


```console:plain実行結果
================================================
[Greeting]

■朝から昼にかけて

　・おはようございます。
　・こんにちは。

■夜に

　・こんばんは。
　・おやすみなさい。
　・さようなら。

================================================
```
```console:html実行結果
Greeting.htmlが作成されました。
```
```html:Greeting.html
<html><head><title>Greeting</title></head><body>
<h1>Greeting</h1>
<p>朝から昼にかけて</p>
<ul>
<li>おはようございます。</li>
<li>こんにちは。</li>
</ul>
<p>夜に</p>
<ul>
<li>こんばんは。</li>
<li>おやすみなさい。</li>
<li>さようなら。</li>
</ul>
</body></html>

```

##クラス図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326131/d4ac2f91-011a-2911-65f9-b6c009dacdd7.png)


##所感
+ 構造を抽象化し、かつDirectorクラスで具体的なサブクラスの記述がないことで入れ替えことができるメリットを交換可能性と呼ぶことを学んだ。
+ またIO操作の処理周りを学んだ。
+ Kotlinについては下記の点を学ぶことができた

1. kotlinだとString#equalsは`==`で表し、内部でequalsを実行していて、参照の比較を行いたい場合は`===`を使用することを学んだ。
2. Array等のコレクションをfor文で使用する場合は末数を数えないuntilが向いていることを学んだ。
3. また、String Template(文字列補間)や、遅延評価、java.io.File#printWriter()について学んだ。

##参考
下記を参考にさせて頂き、大変読みやすく、理解しやすかったです。

[Kotlin – Create File – Examples](https://www.tutorialkart.com/kotlin/kotlin-create-file/)
[KotlinでAndroid開発！　基本構文をおさえよう](https://tech.pjin.jp/blog/2017/03/01/android_with_kotlin_basicsyntax/)
[JavaプログラマがKotlinで便利だと感じること](https://qiita.com/koher/items/cb91dbbff9b14575f498)
[Kotlinで初期化を遅延する](https://re-engines.com/2018/11/15/kotlinで初期化を遅延する/)
[I/O(ファイル,ネットワーク等)](https://sites.google.com/site/memo73737/kotlin/io#TOC-printWriter)
[Kotlin - toString, equals, hashCode](https://blog.y-yuki.net/entry/2019/05/29/003000)