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

# はじめに
このシリーズでは、Java8で導入されたラムダ式・Stream APIを利用した、デザインパターンの実装方法を検討していきます。
サンプルコードは[GitHub](https://github.com/yonetty/java8dp)にアップしています。

# 今回のパターン
[前回の記事](http://qiita.com/yonetty/items/baf989259a51cedd7536)で`Null Object`パターンに言及しましたが、説明は割愛したので今回はこれを取り上げます。
`Null Object`パターンとは、null参照の代わりに`特定のインターフェースを実装するが、実際には何もしない(副作用を生じない)`オブジェクトを参照することで、nullチェック不要で安全なコードを記述するテクニックです。

# 従来の実装
以下のインターフェースに対して処理を委譲するクラスを想定します。

```java:Discount.java
public interface Discount {
    Integer calcDiscount(Integer totalAmount);
}
```

`Dicsount`の実装クラスのインスタンスへの参照はコンストラクタやsetterで与えられるとして、それを利用する際にはnullの場合を考慮して以下のようなnullチェックが必要となるでしょう。

```java:OrderService.java(1)
    private Discount discount;

    public Integer getPrice(Integer amount) {
        if (discount != null) {
            amount -= discount.calcDiscount(amount);
        }
        return amount;
    }
```

もし複数の箇所で`discount`変数を使用する場合、いちいちnullチェックを行うのは面倒です。
そこで、以下のように何もしない(正確には、利用側の処理に影響を及ぼさないように処理をする)インスタンス、つまり`Null Object`を設定します。
`Null Object`は内部クラスとして定義したり、以下のように匿名内部クラスとすることも多いです。

```java:OrderService.java(2)
    private Discount discount2 = new Discount() {
        @Override
        public Integer calcDiscount(Integer totalAmount) {
            return 0;
        }
    };

    public Integer getPrice2(Integer amount) {
        amount -= discount2.calcDiscount(amount);
        return amount;
    }
```

ところで`Discount`インターフェースには`@FunctionalInterface`は付けていませんが、メソッドがひとつのみのインターフェースなので関数型インターフェースとして扱うことができます。
つまり、以下のようにラムダ式を用いて`Null Object`を定義することもできます。

```java:OrderService.java(3)
    private Discount discount3 = amount -> 0;

    public Integer getPrice3(Integer amount) {
        amount -= discount3.calcDiscount(amount);
        return amount;
    }
```

# 関数型の`Null Object`
以下のサンプルは、日付を操作するユーティリティの例です。

```java:DateTimeBuilder.java
public class DateTimeBuilder {
    private LocalDateTime base;
    private Function<Temporal, Temporal> func;

    private DateTimeBuilder(LocalDateTime base) {
        this.base = base;
        func = t -> t;
    }

    private void compose(TemporalAdjuster next) {
        func = func.andThen(t -> next.adjustInto(t));
    }

    public DateTimeBuilder nextMonth() {
        compose(t -> t.plus(1, ChronoUnit.MONTHS));
        return this;
    }

    public DateTimeBuilder lastDay() {
        compose(TemporalAdjusters.lastDayOfMonth());
        return this;
    }

    public LocalDateTime get() {
        return (LocalDateTime) func.apply(base);
    }

    public static DateTimeBuilder of(LocalDateTime base) {
        DateTimeBuilder builder = new DateTimeBuilder(base);
        return builder;
    }
}
```

使用例は以下のようになります。以下のコードでは、引数で与えた日付(`2017/1/17`)の翌月末日を求めています。

```java:Usage
        LocalDateTime base = LocalDateTime.of(2017, 1, 17, 0, 0);
        LocalDateTime dt = DateTimeBuilder.of(base)
                .nextMonth()
                .lastDay()
                .get();
        assertEquals(LocalDateTime.of(2017, 2, 28, 0, 0), dt);
```

`DateTimeBuilder`では、`nextMonth()`や`lastDay()`が呼ばれる度に該当する日付の変換処理を行う関数をインスタンス変数`func`へ合成していき、最終的に`get()`が呼び出されたタイミングで合成関数を適用しています。
合成を行っているのは以下のコードですが、この時、`func`がnullだった場合に`NullPointerException`が発生しないようにする…という考慮を不要にしたいというのが`Null Object`パターン導入の動機となります。

```java:#composeメソッド
    private void compose(TemporalAdjuster next) {
        func = func.andThen(t -> next.adjustInto(t));
    }
```

というわけで、コンストラクタにおいて`func`に`何もしない関数`を設定します。
`Function<Temporal, Temporal>`型が何もしないということの意味は、引数で受け取った`Temporal`型のオブジェクトをそのまま返す（右から左へ受け流す）ということです。
それはラムダ式で書くと`t -> t`ということですね。

```java:NullObjectにあたる関数
    private Function<Temporal, Temporal> func;

    private DateTimeBuilder(LocalDateTime base) {
        this.base = base;
        func = t -> t;
    }
```

# `Function#identity`は単位元
前述のラムダ式`t -> t`は、`java.util.Function`インターフェースに定義されているstaticメソッド`identity()`を使用した方がよいでしょう。

```java:Function#identity()の使用
    private DateTimeBuilder(LocalDateTime base) {
        this.base = base;
        //func = t -> t;
        func = Function.identity();
    }
```

`Function#identity()`は何もしない関数、つまり入力引数をそのまま返却する関数を返します。この関数は、別の変換関数（`g`とします）に対して左から合成しても、右から合成しても`g`と等しい結果を返す合成関数を得ることができます。
これは数学的に考えると、`Function<Temporal, Temporal>`という系における`単位元`であると言えますね。
実際に`単位元`の英語訳は、`identity element`だそうです。

# まとめ
関数の合成を行う際は、`Function#identity()`を単位元として初期値に使うと、煩雑なnullチェックコードを削減しコードの見通しをよくすることができそうです。