システム開発で思うところ

Java EEを主にシステム開発をしながら思うところをツラツラを綴る

EnumのInterfaceに共通処理を実装する

以前の記事で、Interfaceとユーティリティのペアを説明しました。
Enumに状態を保持して、ユーティリティで振舞いを実装する、というやり方です。

vermeer.hatenablog.jp

ロジックの冗長さを避けようと思ったわけですがオブジェクト指向ってそうじゃないんだよね、とも思います。オブジェクト指向至上主義ではありませんが、ユーティリティを使うやり方は、気を抜くとユーティリティまみれになり兼ねないです*1

というわけで、前回の実装をベースに、Interfaceで出来そうな範囲の実装をしてユーティリティクラスを使わない実装を考えてみたいと思います。

実装:staticメソッドを追加する

Interface

package com.mycompany.samples.enumclass.test;

import lombok.NonNull;

/**
 * propertiesの仕様を前提として拡張をしたEnumクラスを共通操作するためのInterface
 *
 * @author Yamashita
 */
public interface EnumCodePropertyInterface {

    public Object getCode();

    public String getProperty();

    /**
     * properties用に拡張したEnumのcode値から取得した拡張Enumを生成する
     *
     * @param <E> EnumCodePropertyInterfaceで拡張したEnumClass
     * @param enumType 生成対象となるEnumClassの型となるClass
     * @param code テーブルや定数として指定しているcode値
     * @return codeに一致するEnumクラス
     */
    public static <E extends Enum<E> & EnumCodePropertyInterface> E codeOf(Class<E> enumType, @NonNull Object code) {
        for (E type : enumType.getEnumConstants()) {
            if (type.getCode().equals(code)) {
                return type;
            }
        }
        throw new IllegalArgumentException("enum class has not code : " + code.toString());
    }

    /**
     * properties用に拡張したEnumのpropertyKey値から取得した拡張Enumを生成する
     *
     * @param <E> EnumCodePropertyInterfaceで拡張したEnumClass
     * @param enumType 生成対象となるEnumClassの型となるClass
     * @param property propertiesの検索に使用するpropertyKey
     * @return propertyKeyに一致するEnumクラス
     */
    public static <E extends Enum<E> & EnumCodePropertyInterface> E propertyOf(Class<E> enumType, @NonNull String property) {
        for (E type : enumType.getEnumConstants()) {
            if (type.getProperty().equals(property)) {
                return type;
            }
        }
        throw new IllegalArgumentException("enum class has not propertyKey : " + property);
    }

}

EnumClass

package com.mycompany.samples.enumclass.test;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;

@AllArgsConstructor @Getter
public enum Gender implements EnumCodePropertyInterface {
    MALE(1, "男"),
    FEMALE(2, "女");

    private final Integer code;
    private final String property;

    public static Gender codeOf(@NonNull Object code) {
        return EnumCodePropertyInterface.codeOf(Gender.class, code);
    }

    public static Gender propertyOf(@NonNull String property) {
        return EnumCodePropertyInterface.propertyOf(Gender.class, property);
    }
}

使用例

    public void exec() {
        Gender gender = Gender.codeOf(Gender.FEMALE.getCode());
        System.out.println("gender.getCode() = " + gender.getCode());
        System.out.println("gender.getProperty() = " + gender.getProperty());
    }

さいごに

staticによる実装なので、結局のところユーティリティクラスを内包しただけとも言えるかもしれません*2。メリットは使用するEnumClassに仕様が凝集していて使用時の実装もすっきりと読みやすくはなっているように思います。なによりもEnumvalueOfと並列な感じで個人的には分かりやすいように思います。デメリットは、Enumを定義する際に儀式的にメソッドを実装しないといけないというところです。*3

後日追記

自分なりにAnnotationProcessorによる実験的な実装も検討してみましたが素直にユーティリティを使うやり方がシンプルで良さそうだ、という結論に至りました*4

*1:必然性があれば、ユーティリティは悪ではないですが、やり易いからという理由でユーティリティを多用するのは、基本的に良くないと思っています

*2:おそらく実際そうです

*3:冗長さを解消するためにはPluggable Annotation Processing APIが解な気がします

*4:lombokの拡張が自分のイメージに一番近いのですが、そこまででも無いかな、と