Scalaでデザインパターン ~Builderパターン~

# はじめに
こんにちは。
会社では「鍋島」と呼ばれていますが、この名前もようやく慣れてきました。

[前回はStrategyパターン](https://qiita.com/yamato3310/items/1364577d5d2f60297d72)の記事を投稿しました。
今回も同様に、次のデザインパターンについての説明をしていきたいと思います。
~~また買い物シリーズです~~

# Builderパターン
今回はBuilderパターンの説明をします。
Builderパターンとは、複雑なインスタンスを作る際にそのクラスを生成するためのBuilderクラスを作ります。

たとえば、Aというクラスのインスタンスを作るとしましょう。
Aというクラスは非常に多くのパラメータがあるとします。
これを毎回すべてコンストラクタに渡すと、このような問題が起こることがあります。

- パラメータが多すぎてソースコードの見通しが悪くなってしまう
- 必須のパラメータが一部しかないときも、コンストラクタですべて設定をする必要が出てきてしまう

そういった時にBuilderパターンを利用します。

Builderパターンを利用した場合、その問題となっている処理をBuilderクラスで行うようすることで、そのクラスを使う側は不要な処理を書く必要がなくなります。

このように、コードの見通しが良くなり、インスタンス作成が楽になります。

# サンプルコード
例えば買い物に行った際に、レジで会計をしてレシートができるフローを考えてみましょう。
レジでは商品をバーコードリーダーで、一つずつ読み取っていきます。
そして読み取った商品をレシートに記録をし、すべての会計が完了するとレシートを出力します。
この場合、「レシート」インスタンスを構築するためのパラメータ（商品）の数が不定とななります。
このケースは、よりBuilderパターンが有効に作用すると考えられます。

更に今回は、会計中の一部で商品の購入をキャンセルするパターンも考慮します。
この場合、Builderに対して商品の削除を行います。
では、早速実装してみましょう。

![Screenshot from 2020-01-21 12-02-25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276450/fb9738c2-44ac-0246-5209-cc8dd3ea4171.png)

```scala
import scala.math._

object Main extends App {
  val products = new ProductRepository().create(10)
  val user = User("nabeshi", 10000, products)
  val register = new Register(new ReceiptBuilder())
  val receipt = register
    .addProducts(user.shoppingBasket)
    .cancelProduct(user.shoppingBasket(1))
    .calculate(user.money)
  receipt.print()
}

// ReceiptBuilderを操作するクラス。ユーザーが商品を買う時はこのクラスを使用する
class Register(builder: ReceiptBuilder) {
  // 商品をbuilderに追加する
  def addProducts(products: Seq[Product]): Register= {
    new Register(products.foldLeft(builder)((builder, product) => builder.addProduct(product)))
  }
  // 商品をキャンセルする
  def cancelProduct(product: Product): Register = {
    new Register(builder.cancelProduct(product))
  }
  // 計算をしてレシートを出力する
  def calculate(money: Int): Receipt = builder.build(money)
}

//　商品を取り出すためのクラス
class ProductRepository() {
  // 商品を取り出す
  def create(num: Int): List[Product] = (1 to num).toList.map(i => Product(s"product$i", floor(random() * 1000).toInt))
}

// 商品
case class Product(name: String, price: Int)

// 購入した商品をまとめるクラス
case class Receipt(products: Seq[Product], payment: Int, price: Int) {
  // レシートを表示する
  def print(): Unit = {
    products.foreach(product => println(s"${product.name}: ${product.price} 円"))
    println(s"合計: $price 円")
    println(s"お預かり: $payment 円")
    println(s"お釣り: ${payment - price} 円")
  }
}

// builderクラス。商品を追加していきbuild関数が呼び出されたタイミングで、レシートを生成している
class ReceiptBuilder(private val productList: Seq[Product] = Seq()) {
  // レジを通した商品を追加していく
  def addProduct(product: Product): ReceiptBuilder = new ReceiptBuilder(productList :+ product)
  // 商品をキャンセルする
  def cancelProduct(product: Product): ReceiptBuilder = new ReceiptBuilder(productList.diff(Seq(product)))
  // レシートを生成する
  def build(money: Int): Receipt = Receipt(productList, money, productList.map(_.price).sum)
}

// 商品を買うユーザー
case class User(name: String, money: Int, shoppingBasket: Seq[Product])

```

# 解説
それでは、サンプルコードの解説をしていきます。
まずは、それぞれのクラス定義について解説します。

- `Register` ここに購入する商品を入れることでレシートを生成します。
- `ProductRepository` ここから商品を取り出します。
- `Product` 商品です。
- `Receipt`　レシートオブジェクトです。このインスタンスを生成します。
- `ReceiptBuilder` 今回のBuilderです。ここでレシートを生成します。

次にBuilderパターンをどのように利用しているかを解説します。
上でも書きましたが、`ReceiptBuilder`が今回のBuilderパターンの肝です。
`ReceipBuilder`によって`Receipt`を作ります。
この`ReceipBuilder`に、バーコードリーダーで読み取った商品を、addProduct関数で追加していきます。
すべての商品を追加し終わったら、accouting関数を呼び出すことでレシートを生成しています。

このようにして、`ReceipBuilder`クラスは、レシート生成するために必要な処理を集約します。

# 最後に
最後まで読んでくださりありがとうございます。
Builderパターンのメリットが伝わりましたでしょうか？
よかったら利用してみてください

以上です。

次回は、FactoryMethodパターンを解説していきます。