カテゴリー
コンピューター

【Java】Enum をもっと有効活用すべくもっと考えてみる。Enum はあるものの様々な側面をクラスひとつで扱えるものだと思った。

はじめに

【Java8 くらい】Enum についてのお勉強。可能性を探る – oki2a24 にて、本当に基本的な Enum クラスの例を出しました。曜日の Enum ですね。

このままでは Enum を使う意味が薄いように感じます。

そこで、もっと便利な使い方を学んでみました。

曜日を題材にした、まとめ

  • 曜日には様々な呼び方、役割、区切り、といった側面がある。
  • そういった各側面を全部 Enum クラスにまとめて扱う。
  • そうすると、プログラムの好きな時に好きな側面を取り出すことができる。

上で “側面” と呼んでいる呼び方や役割、というのは、例えば次のようにまとめられます。

No 側面 日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日
1 コード値 0 1 2 3 4 5 6
2 日本語 日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日
3 日本語略
4 日本語記号 (日) (月) (火) (水) (木) (金) (土)
5 英語 Sunday Monday Tuesday Wednesday Thursday Friday Saturday
6 英語略 Sun. Mon. Tue. Wed. Thu. Fri. Sat.
7
8 価格設定 80% 100% 100% 100% 100% 100% 90%

以下では、これらの側面の中で、コード値、日本語略、価格設定を Java で定義し、好きな側面を好きなタイミングで取得するサンプルコードを掲載します。

サンプルコード

実際に動かすと次のようになります。

Enum を使った場合
1350.0
表示文字列: 土, コード: 6
Class を使った場合
1350.0
表示文字列: 土, コード: 6
どちらも使わなかった場合
1350.0
表示文字列: 土, コード: 6

ファイルは次となります。

  • App.java
  • DayOfTheWeekEnum.java
  • DayOfTheWeekClass.java

DayOfTheWeekClass.java は、Enum クラスを通常の Java クラスで表現しようとしたものになります。その結果、次のように感じました。

  • Enum を使った場合と、クラスを使った場合とで、今回はあまり差を感じなかった。
  • クラスを使った場合の方が、都度インスタンス化するため、きっと僅かにメモリの節約になる。ただし、都度インスタンス化するため、各側面の値は同じでも、オブジェクトとしては異なる。何度も曜日を呼び出していると、簡単に Enum のときよりもメモリ使用量が増えることが予想される。
  • クラスを使った場合、今回はコード値からオブジェクトを生成した。これを略記号等からも生成したい場合、もう一つ別の switch 文を書く必要がある。これは極めて面倒だし、メンテ性が下がる。では予め値をセットしたインスタンスを用意しておけばと考えたが、それでは Enum と同じ構造になってくる。だったら最初から Enum 使えばいいじゃん。

コードは、次からです。

import java.math.BigDecimal;

public class App {
    public static void main(String[] args) {
        System.out.println("Enum を使った場合");
        new App().useEnum();
        System.out.println("Class を使った場合");
        new App().useClass();
        System.out.println("どちらも使わなかった場合");
        new App().notUseEnum();
    }

    public void useEnum() {
        // ウェブブラウザ画面から曜日コードが渡されてきたとする
        Integer codeValue = Integer.valueOf(6);
        // Java プログラム内で扱うために enum 定数に変換
        DayOfTheWeekEnum day = DayOfTheWeekEnum.getByCode(codeValue);

        // 例えば買い物のプログラムだとして、曜日ごとの割引をすると次のようになる
        BigDecimal price = new BigDecimal(1500);
        BigDecimal discounted = price.multiply(day.discountCoefficient());
        System.out.println(discounted);

        // 再び画面に曜日の情報を渡したい、具体的には表示文字列とコードを渡したいなら次のようになる
        String dayString = day.shorthand();
        Integer dayCode = day.code();
        System.out.println(String.format("表示文字列: %s, コード: %s", dayString, dayCode));
    }

    public void useClass() {
        // ウェブブラウザ画面から曜日コードが渡されてきたとする
        Integer codeValue = Integer.valueOf(6);
        // Java プログラム内で扱うために enum 定数に変換
        DayOfTheWeekClass day = DayOfTheWeekClass.getByCode(codeValue);

        // 例えば買い物のプログラムだとして、曜日ごとの割引をすると次のようになる
        BigDecimal price = new BigDecimal(1500);
        BigDecimal discounted = price.multiply(day.discountCoefficient());
        System.out.println(discounted);

        // 再び画面に曜日の情報を渡したい、具体的には表示文字列とコードを渡したいなら次のようになる
        String dayString = day.shorthand();
        Integer dayCode = day.code();
        System.out.println(String.format("表示文字列: %s, コード: %s", dayString, dayCode));
    }

    public void notUseEnum() {
        // ウェブブラウザ画面から曜日コードが渡されてきたとする
        Integer codeValue = Integer.valueOf(6);

        // 準備
        String shorthand = null;
        BigDecimal discountCoefficient = null;
        switch (codeValue) {
          case 0:
              shorthand = "日";
              discountCoefficient = new BigDecimal("0.8");
              break;
          case 1:
              shorthand = "月";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 2:
              shorthand = "火";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 3:
              shorthand = "水";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 4:
              shorthand = "木";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 5:
              shorthand = "金";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 6:
              shorthand = "土";
              discountCoefficient = new BigDecimal("0.9");
              break;
          default:
              throw new IllegalArgumentException();
        }
        // 例えば買い物のプログラムだとして、曜日ごとの割引をすると次のようになる
        BigDecimal price = new BigDecimal(1500);
        BigDecimal discounted = price.multiply(discountCoefficient);
        System.out.println(discounted);

        // 再び画面に曜日の情報を渡したい、具体的には表示文字列とコードを渡したいなら次のようになる
        String dayString = shorthand;
        Integer dayCode = codeValue;
        System.out.println(String.format("表示文字列: %s, コード: %s", dayString, dayCode));
    }
}
import java.math.BigDecimal;
import java.util.stream.Stream;

public enum DayOfTheWeekEnum {
    SUNDAY(0, "日", new BigDecimal("0.8")),
    MONDAY(1, "月", new BigDecimal("1.0")),
    TUESDAY(2, "火", new BigDecimal("1.0")),
    WEDNESDAY(3, "水", new BigDecimal("1.0")),
    THURSDAY(4, "木", new BigDecimal("1.0")),
    FRIDAY(5, "金", new BigDecimal("1.0")),
    SATURDAY(6, "土", new BigDecimal("0.9"));

    /** 曜日コード */
    private final Integer code;

    /** 略称 */
    private final String shorthand;

    /** 割引係数 */
    private final BigDecimal discountCoefficient;

    DayOfTheWeekEnum(Integer code, String shorthand, BigDecimal discountCoefficient) {
        this.code = code;
        this.shorthand = shorthand;
        this.discountCoefficient = discountCoefficient;
    }

    public Integer code() {
        return code;
    }

    public String shorthand() {
        return shorthand;
    }

    public BigDecimal discountCoefficient() {
        return discountCoefficient;
    }

    public static DayOfTheWeekEnum getByCode(Integer code) {
        return Stream.of(DayOfTheWeekEnum.values())
            .filter(v -> v.code.equals(code))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException());
    }
}
import java.math.BigDecimal;

public class DayOfTheWeekClass {
    /** 曜日コード */
    private final Integer code;

    /** 略称 */
    private final String shorthand;

    /** 割引係数 */
    private final BigDecimal discountCoefficient;

    private DayOfTheWeekClass(Integer code) {
        this.code = code;
        switch (code) {
          case 0:
              shorthand = "日";
              discountCoefficient = new BigDecimal("0.8");
              break;
          case 1:
              shorthand = "月";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 2:
              shorthand = "火";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 3:
              shorthand = "水";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 4:
              shorthand = "木";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 5:
              shorthand = "金";
              discountCoefficient = new BigDecimal("1.0");
              break;
          case 6:
              shorthand = "土";
              discountCoefficient = new BigDecimal("0.9");
              break;
          default:
              throw new IllegalArgumentException();
        }
    }

    public Integer code() {
        return code;
    }

    public String shorthand() {
        return shorthand;
    }

    public BigDecimal discountCoefficient() {
        return discountCoefficient;
    }

    public static DayOfTheWeekClass getByCode(Integer code) {
      return new DayOfTheWeekClass(code);
    }
}

おわりに

随分と Enum に馴染めてきたような気がします。

最初は、区分オブジェクトを理解したいためにあれこれいじってきましたけれども、あるものの持つ様々な特徴や属性といった側面をまるごと1つのクラスで扱うために Enum を使うのは結構便利だと思いました。

まだよちよちなので、落とし穴はあるかもしれませんけれども、使っていれば解っていくと思いますので様々試してみたいと思います。

なお、区分オブジェクトについては、次の書籍で紹介されているものです。

    • 第2章 場合分けのロジックを整理する

また、最後にさらっと書きますけれども、区分オブジェクトとは、今回紹介した各側面の1つとして、業務ロジックがある、というだけだとわたくしは理解しております。

次のページの使い方も非常に参考になりました。ありがとうございます♪

以上です。

コメントを残す