Java8 ラムダ式&Streamによるデザインパターン再考 - Builderパターン -

# はじめに
Java8にてラムダ式とStream APIが導入され、Java言語にもようやく関数型プログラミングのパラダイムが持ち込まれました。これにより設計の`定石`も変わりつつあります。
Stream APIとラムダ式に関しては、オライリーから出ている良書『[Javaによる関数型プログラミング](https://www.oreilly.co.jp/books/9784873117041/)』で学ぶのがよいと思いますが、この書籍の4章「ラムダ式で設計する」ではラムダ式を活用した新しい設計のアイデアが紹介されています。
本記事では、上記書籍にインスパイアされた筆者が、ラムダ式とStreamを用いたデザインパターンの新しい実装方法を検証、考察します。

# 今回のパターン
GoFの生成に関するパターンのひとつ、`Builder`パターンを考察します。
Javaでの`Builder`パターンの実装については [こちらのページ](http://qiita.com/disc99/items/840cf9936687f97a482b)にまとめられていますのでご参照ください。

# 流暢なインターフェースの改善
『Javaによる関数型プログラミング』では以下のテクニックが紹介されています。

- コンストラクタをprivateにして直接インスタンス化されるのを防ぐ
- 代わりに、`Consumer<T>`型の関数を引数に受け取るstaticメソッドを用意する

```java:FluentBuilder
public class MailBuilder {

    private String fromAddress = "";
    private String toAddress = "";
    private List<String> ccAddresses = new ArrayList<>();
    private String subject = "";
    private String body = "";

    private MailBuilder() {
    }

    public MailBuilder from(String address) {
        this.fromAddress = address;
        return this;
    }

    public MailBuilder to(String address) {
        this.toAddress = address;
        return this;
    }

    public MailBuilder cc(String address) {
        this.ccAddresses.add(address);
        return this;
    }

    public MailBuilder subject(String subject) {
        this.subject = subject;
        return this;
    }

    public MailBuilder body(String body) {
        this.body = body;
        return this;
    }

    private void doSend() {
        StringBuilder sb = new StringBuilder();
        sb.append("TO:").append(toAddress).append("\r\n");
        if (!ccAddresses.isEmpty()) {
            sb.append("CC:").append(String.join(",", ccAddresses)).append("\r\n");
        }
        sb.append("FROM:").append(fromAddress).append("\r\n");
        sb.append("SUBJECT:").append(subject).append("\r\n");
        sb.append("BODY:").append(body).append("\r\n");
        System.out.println(sb.toString());
    }

    public static void send(final Consumer<MailBuilder> consumer) {
        final MailBuilder mailer = new MailBuilder();
        consumer.accept(mailer);
        mailer.doSend();
    }
```

上記`Builder`の使用例は以下のようになります。

```java:FluentBuilder-Usage
        MailBuilder.send(mailer -> {
            mailer.from("fowler@example.com")
                    .to("trump@example.com")
                    .subject("Greeting")
                    .body("Hello, Mr. President!");
        });
```

従来の`流暢なインターフェース`で組み立てを行うBuilderと比較したメリットは以下とされています。

- `new`キーワードを使用しないので、より可読性が高く流暢である
- `Builder`のインスタンスの参照スコープが、staticメソッドに渡すコードブロック内に限定される

# 問題点
`流暢なインターフェース`型の`Builder`を使っていて不便だなと思うのは、条件によってメソッドの呼び出しを切り替えたい場合や、繰り返してメソッドを呼び出したい場合などです。
Javaの言語仕様上、ドット`.`で連結したメソッドチェーンの中に制御構文を埋め込むことはできませんから、メソッドチェーンを途中で分断してif文やfor文を用いて制御することとなり、結果として`流暢`でなくなってしまいます。
この問題をうまく解決できないでしょうか？

# 条件制御を組み込む
条件式が`true`の場合のみ、`Consumer<T>`を呼び出すメソッドを追加します。

```java:MoreFluentBuilder
    public MailBuilder doIf(boolean condition, final Consumer<MailBuilder> consumer) {
        if (condition) {
            consumer.accept(this);
        }
        return this;
    }
```

ラムダ式がネストされた形となってしまうものの、メソッドチェーンを分断せずに条件制御を組み込むことができました。

```java:MoreluentBuilder-Usage
        MailBuilder.send(mailer -> {
            mailer.from("fowler@example.com")
                    .to("trump@example.com")
                    .doIf(someCondition(), m -> m.cc("clinton@example.com"))
                    .subject("Greeting")
                    .body("Hello, Mr. President!");
        });
```

# 繰り返し制御を組み込む
今度は関数型を2つ引数に取る必要があるので若干複雑です。
まずは繰り返し対象となるものを、`Iterable<T>`型で受け取ります。
そして2つ目の引数は`BiConsumer<T,U>`です。繰り返しの要素(`T`型のインスタンス)と`Builder`インスタンスの参照を受け取って処理する必要があるので、`Consumer<T>`ではなく`BiConsumer<T,U>`としているのがポイントです。

```java:MoreFluentBuilder2
    public <T> MailBuilder foreach(Iterable<T> iterable, final BiConsumer<MailBuilder, T> consumer) {
        iterable.forEach(t -> consumer.accept(this, t));
        return this;
    }
```

以下のように、メソッドチェーンを分断せずに繰り返し制御を埋め込むことができました。

```java:MoreFluentBuilder2-Usage
        final List<String> ccAddresses = Arrays.asList("clinton@example.com", "cockburn@example.com");
        MailBuilder.send(mailer -> {
            mailer.from("fowler@example.com")
                    .to("trump@example.com")
                    .foreach(ccAddresses, (m, ccAddress) -> m.cc(ccAddress))
                    .subject("Greeting")
                    .body("Hello, Mr. President!");
        });
```

# まとめ
ラムダ式をうまく活用すると、よりすっきりして見通しのよいコードを書くことが可能となります。
今回は`流暢なインターフェース`型の`Builder`パターンの実装を、もっと流暢にしてみる方法を検討してみました。