Javaのenumのエレガントな使い方を探しています。
これまで、①「定数を型安全かつ便利に使えるクラス」ぐらいの認識しかなく、そこまで価値を感じていませんでした。しかし、②「ストラテジーパターンを利用したいわゆる定数固有メソッド」(effective javaに載っているようなやつ)の存在を知ってから、次第にenumに興味を持つようになりました。
そこで質問です。実際の現場で見たことのあるenumのエレガントな使い方を教えてください。デザインパターンが盛り込まれてクラス数が多い重厚なやつでもいいですし、手軽に使えるシンプルな小技でもいいです。
インターネットで色々調べましたが、①もしくは②の説明が多いと感じましたので、これら以外の事例を希望します。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
ベストアンサー
Java の Enum は手軽にインターフェースを実装し、ポリモフィズムを作れるところが便利です。
Java
1/** 2 * 会員 3 */ 4class Member { 5 6 private int id; 7 8 private String name; 9 10 private MemberRank memberRank; 11 12 // アクセサ略 13} 14 15/** 16 * 会員ランクを表す列挙 17 */ 18enum MemberRank { 19 20 /** 一般会員 */ 21 GENERAL { 22 @Override 23 public BigDecimal getPrice(BigDecimal catalogPrice) { 24 // 一般会員は定価のまま 25 return catalogPrice; 26 } 27 }, 28 29 /** シルバー会員 */ 30 SILVER { 31 @Override 32 public BigDecimal getPrice(BigDecimal catalogPrice) { 33 // 5%引き 34 BigDecimal discountPrice = catalogPrice.multiply(new BigDecimal("0.05")); 35 return catalogPrice.subtract(discountPrice); 36 } 37 }, 38 39 /** ゴールド会員 */ 40 GOLD { 41 @Override 42 public BigDecimal getPrice(BigDecimal catalogPrice) { 43 // 10%引き 44 BigDecimal discountPrice = catalogPrice.multiply(new BigDecimal("0.1")); 45 return catalogPrice.subtract(discountPrice); 46 } 47 }; 48 49 /** 50 * @param catalogPrice 定価 51 * @return ランクに応じた金額 52 */ 53 public abstract BigDecimal getPrice(BigDecimal catalogPrice); 54 55}
このようなものを用意しておくと、ランク毎に if を切ることなく、
拡張性が高いコードになります。
Java
1// getMember はセッションなどからログイン中の会員情報を取得するものとする 2BigDecimal price = getMember().getMemberRank().getPrice(catalogPrice);
###追記1
MemberクラスのmemberRankプロパティに、各会員の状態を持たせる場合、どのような実装があり得ますか?
問題はこれです。
通常よくあるのは、データベースの会員情報から MemberRank を作成したいわけですから、
O/Rマッパーが Member取得時に自動で入れてくれたりもします。
データベースの値を MemberRank Enum に変換してくれます。
そういうのを省いて考えた場合どうなるかというと
例えば、以下のような感じだと台無しなわけです。
java
1if (rank.equals("general")) { 2 member.setMemberRank(MemberRank.GENERAL); 3} else if (rank.equals("silver")) { 4 member.setMemberRank(MemberRank.SILVER); 5} else if (rank.equals("gold")) { 6 member.setMemberRank(MemberRank.GOLD); 7}
こういった場合は、MemberRank に以下のようなファクトリーメソッドを用意するといいです。
java
1public static MemberRank of(String name) { 2 return MemberRank.valueOf(name.toUpperCase()); 3}
そうすると、以下のように設定できます。
java
1// rank は、"general", "silver", "gold" の文字列 2member.setMemberRank(MemberRank.of(rank));
データに "general" などの文字列ではなく、
一般会員 = 1, シルバー会員 = 2, ゴールド会員 = 3 などのように
数値として持ちたいという事も多いでしょう。
その時は、MemberRankを以下のようにします。
java
1/** 2 * 会員ランクを表す列挙 3 */ 4enum MemberRank { 5 6 /** 一般会員 */ 7 GENERAL(1) { 8 @Override 9 public BigDecimal getPrice(BigDecimal catalogPrice) { 10 // 一般会員は定価のまま 11 return catalogPrice; 12 } 13 }, 14 15 /** シルバー会員 */ 16 SILVER(2) { 17 @Override 18 public BigDecimal getPrice(BigDecimal catalogPrice) { 19 // 5%引き 20 BigDecimal discountPrice = catalogPrice.multiply(new BigDecimal("0.05")); 21 return catalogPrice.subtract(discountPrice); 22 } 23 }, 24 25 /** ゴールド会員 */ 26 GOLD(3) { 27 @Override 28 public BigDecimal getPrice(BigDecimal catalogPrice) { 29 // 10%引き 30 BigDecimal discountPrice = catalogPrice.multiply(new BigDecimal("0.1")); 31 return catalogPrice.subtract(discountPrice); 32 } 33 }; 34 35 /** 各列挙が持つ値 */ 36 private int value; 37 38 /** 39 * コンストラクタ 40 */ 41 private MemberRank(int value) { 42 this.value = value; 43 } 44 45 /** 46 * @param catalogPrice 定価 47 * @return ランクに応じた金額 48 */ 49 public abstract BigDecimal getPrice(BigDecimal catalogPrice); 50 51 /** 52 * ファクトリー 53 */ 54 public static MemberRank of(int value) { 55 return Stream.of(MemberRank.values()).filter(r -> r.value == value) 56 .findFirst().orElseThrow(IllegalArgumentException::new); 57 } 58}
そうすると、以下のように設定できます。
java
1// rank は、1, 2, 3 の数値 2member.setMemberRank(MemberRank.of(rank));
###追記2
getPriceのようなメソッドを他にもいくつか追加したい場合、どういったクラス構成が望ましいですか?
会員ランク毎に必要な振る舞いであれば、MemberRankで全て実装すべきです。
例えば以下は、購入金額から付与されるポイントを計算するメソッドと
特別会員かどうかを判断するメソッドを追加してみました。
これは、特別会員だけが見れる項目や、特別会員専用ページなどの制御で使うことを想定しています。
java
1/** 2 * 会員ランクを表す列挙 3 */ 4enum MemberRank { 5 6 /** 一般会員 */ 7 GENERAL(1) { 8 @Override 9 public BigDecimal getPrice(BigDecimal catalogPrice) { 10 // 一般会員は定価のまま 11 return catalogPrice; 12 } 13 14 @Override 15 public BigDecimal getPoint(BigDecimal totalPrice) { 16 // 一般会員は購入金額の1%をポイント付与 17 BigDecimal percentage = new BigDecimal("0.01"); 18 return totalPrice.multiply(percentage).setScale(0, BigDecimal.ROUND_DOWN); 19 } 20 21 @Override 22 public boolean isSpecialMember() { 23 // 一般会員は特別会員でない 24 return false; 25 } 26 }, 27 28 /** シルバー会員 */ 29 SILVER(2) { 30 @Override 31 public BigDecimal getPrice(BigDecimal catalogPrice) { 32 // 5%引き 33 BigDecimal discountPrice = catalogPrice.multiply(new BigDecimal("0.05")); 34 return catalogPrice.subtract(discountPrice); 35 } 36 37 @Override 38 public BigDecimal getPoint(BigDecimal totalPrice) { 39 // シルバー会員は購入金額の2%をポイント付与 40 BigDecimal percentage = new BigDecimal("0.02"); 41 return totalPrice.multiply(percentage).setScale(0, BigDecimal.ROUND_DOWN); 42 } 43 44 @Override 45 public boolean isSpecialMember() { 46 // シルバー会員は特別会員でない 47 return false; 48 } 49 }, 50 51 /** ゴールド会員 */ 52 GOLD(3) { 53 @Override 54 public BigDecimal getPrice(BigDecimal catalogPrice) { 55 // 10%引き 56 BigDecimal discountPrice = catalogPrice.multiply(new BigDecimal("0.1")); 57 return catalogPrice.subtract(discountPrice); 58 } 59 60 @Override 61 public BigDecimal getPoint(BigDecimal totalPrice) { 62 // ゴールド会員は購入金額の3%をポイント付与 63 BigDecimal percentage = new BigDecimal("0.03"); 64 return totalPrice.multiply(percentage).setScale(0, BigDecimal.ROUND_DOWN); 65 } 66 67 @Override 68 public boolean isSpecialMember() { 69 // ゴールド会員は特別会員 70 return true; 71 } 72 }; 73 74 /** 75 * @param catalogPrice 定価 76 * @return ランクに応じた金額 77 */ 78 public abstract BigDecimal getPrice(BigDecimal catalogPrice); 79 80 /** 81 * @param totalPrice 購入合計金額 82 * @return ランクに応じたポイント数 83 */ 84 public abstract BigDecimal getPoint(BigDecimal totalPrice); 85 86 /** 87 * 特別会員かどうかを返す。 88 * <pre> 89 * 特別会員とはゴールド会員以上の会員のこと。 90 * (現状、ゴールド会員が最高ランクだが、来年にはプラチナ会員を導入予定) 91 * </pre> 92 * 93 * @return true..特別会員 94 */ 95 public abstract boolean isSpecialMember(); 96 97 /** 各列挙が持つ値 */ 98 private int value; 99 100 /** 101 * コンストラクタ 102 */ 103 private MemberRank(int value) { 104 this.value = value; 105 } 106 107 /** 108 * ファクトリー 109 */ 110 public static MemberRank of(int value) { 111 return Stream.of(MemberRank.values()).filter(r -> r.value == value) 112 .findFirst().orElseThrow(IllegalArgumentException::new); 113 } 114}
投稿2016/04/05 05:05
編集2016/04/06 04:40総合スコア4666
0
①と②の延長線上だと思いますが、個人的によく使うのが下記です。
モードなどを指定するメソッド
→int型ではなくEnumを良く使います。
(タイプセーフなメソッドを作れる。言い方は悪いですが、レベルが低いプログラマでも変な書き方をさせないことができます。int型だからといって全く関係のないint型の定数を代入していたりする人も居たり。。。)
データベースを更新するようなメソッドでDBとカラムを指定するためにEnumとEnumMapを利用
・update(DB名.Column名, String value)
└どのDBのカラムをどの値でアップデートするかを指定
→update(EnumMap<DB名.Column名, value>)
└複数のカラムをアップデートするときはこちらを利用
アップデートするための似たようなメソッドが大量にごろごろ転がっていたのですが、これですっきりしました。
投稿2016/04/02 15:20
総合スコア907
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
調べてみたところ、enum
をシングルトンパターンで利用する、というアイデアを見つけました。
http://stackoverflow.com/questions/26285520/implementing-singleton-with-an-enum-in-java
enum
がインスタンス化不可能(かつ継承不可能)であることを利用したもののようです。
投稿2016/04/02 15:02
総合スコア4791
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
SwingのJComboBoxの選択候補にenumを適用したことがあります。簡潔に書けました。
http://d.hatena.ne.jp/torutk/20080105/p1
投稿2016/04/05 07:36
総合スコア194
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
よく使うのは、こういう表現でしょうか?
enum Country {
JAPAN("JP", "JPN"),
;
public final String alpha2;// ISOの2文字 国コード
public final String alpha3; // ISOの3文字 国コード
public Country(String alpha2, String alpha3) {
this.alpha2 = alpha2;
this.alpha3 = alpha3;
}
}
これで、
Country.JAPAN.alpha2.equals("JP"); // true
Country.JAPAN.alpha3.equals("JPN"); // true
と出来ます。
※ iPhoneからの書き込みで面倒なので、アクセッサーメソッドは省略しています(アクセッサーを作ることの是非もありますし)
投稿2016/04/04 14:10
総合スコア30
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/04/05 15:04
2016/04/05 16:32
2016/04/06 03:46 編集
2016/04/06 04:44
2016/04/13 12:34