本文へスキップ
BecomeCoder

Javaデザインパターンコース · 第5章 モダンJavaとパターン ― 言語機能で置き換えられるもの · レッスン15

関数インタフェース + ラムダ ― Strategyを1行にする

ローカル実施

導入

第4章のStrategyパターンでは、NoDiscountPercentDiscountFixedDiscount と、割引方法1つにつきクラスを1つ作りました。ちゃんとした設計ですが、「たった1行の計算式」のためにクラスファイルを1つ用意するのは大げさに感じることもあります。Javaの 関数インタフェースラムダ式 を使うと、Strategyの考え方そのままに、実装をぐっと短く書けます。

説明

メソッドが1つしかないインタフェース(関数インタフェース)は、クラスを定義せずに ラムダ式 でその場に実装を書けます。

interface DiscountStrategy {
    int apply(int price);
}

public class Main {
    public static void main(String[] args) {
        // クラスを作らず、ラムダ式でその場に実装を渡す
        Cart cart = new Cart(price -> price - price * 10 / 100);
        System.out.println(cart.checkout(1000));   // 900

        // 固定額オフも、その場で書ける
        cart.setStrategy(price -> price - 200);
        System.out.println(cart.checkout(1000));   // 800
    }
}
graph LR
  A["従来: PercentDiscount クラスを定義"] --> B["Cart に new PercentDiscount(10) を渡す"]
  C["ラムダ: price -> price - price * 10 / 100"] --> D["Cart にその場で渡す(クラス定義不要)"]
  • price -> price - price * 10 / 100DiscountStrategy の唯一のメソッド apply(int price)中身だけをその場に書いたものです。price が引数、-> の右側が「返す式」です。
  • new Cart(price -> ...) は、実質的に new Cart(new PercentDiscount(10))(第4章のコード)と同じ動作をします。違いは、PercentDiscount というクラスをわざわざファイルとして定義する必要がないことだけです。
  • java.util.function パッケージには Function<T, R>(1引数を受け取り1つ返す)などの汎用的な関数インタフェースが用意されており、自分で DiscountStrategy を定義しなくても済むケースも多くあります。
  • 注意点もあります。ラムダは「その場限りの小さな処理」に向いていますが、状態を持ったり、複数のメソッドが必要だったり、名前をつけて意味を明確にしたい場合は、これまで通りクラス(PercentDiscount のような)で実装したほうが読みやすいこともあります。「短く書けるから常にラムダ」ではなく、状況に応じた使い分けが大切です。