2016/09/01 このエントリーをはてなブックマークに追加 はてなブックマーク - 【Java】定数クラスをどうしたものかと改めて考える

【Java】定数クラスをどうしたものかと改めて考える


いや、大した話じゃないんですけど
みんなどうしてるのかなって思って。



Javaの定数クラス。皆さんの使用状況はこんな感じだろうか。

  • なにそれ?
  • 知ってるけど使ってない
  • 全部propertiesファイルとかxmlとかに持ってるから使わない
  • 使ってる


使うとすればどのように使うのが良いかなとか。
わざわざ考えたりしないような気がするので、あえて考える。





定数クラスとはpublic static finalなフィールドをたくさん持つクラスで、
定数を管理するために用いられる感じ。

ざっくりとこんなの。

見たことある人は「あー、、。。。」となる。

public final class Constants {
    public static final String SLASH = "/";
    public static final String BLANK = " ";
    public static final String CRLF = "¥r¥n";
    public static final String LF = "¥n";
    private Constants (){}
}

ざっと思いつくものを。

  • public finalなクラスであるべき
  • privateコンストラクタを持ちインスタンスは生成しない
  • フィールドはpublic static finalである

しょーじき、privateコンストラクタとかfinalなクラスとかじゃなくてもいいやんとも思える。これは性善説的な考え。

コンパイラの力を利用したいのであれば、こういうコテコテの手法を取ったほうが良いなとも思う。


これも、ざっと思いつくものを。



  • 定数の数が多い

とにかく定数が多い。どこに何があるのか追えない。
コード補完で探すのが一苦労。



  • 名前が分かりにくい

こんなんとか。

public static final String ONE = “1”;
public static final String TWO = “TWO”

この例見て悪い冗談だなぁ…ハハハ…と思った人は幸せものかも知れないですね(意味深)



  • 同じ意味の定数が至るところにある

HogeConstantsとFugaConstantsで同じ定数があるとか。
同じ定数で意味合いが違ったりとかするのでその場合は良いのだけど、
全く同じ定数なのに色んな所に散見されるというのはモデリングの仕方が良くないのかなと思う。
それとか定数クラス以外の普通のクラスのprivate static finalなんちゃらで同じ定数があったりとか。。。
保守性がさがってしまいがち。



  • interfaceで定数を管理する

幸か不幸か、僕は実際のプロジェクトではお目にかかったがない。

でも流行ったらしい。定数をinterfaceに定義してその定数を使う側はimplementsするんだって。
それインターフェースじゃないがな!みたいな気持ちになる。

J2SE 5以降、staticインポートが出来るようになったので、もはやこれをやるメリットも無いし、使い方として良くないしという完全なアンチパターン。
とかいう話は以前にcero_tさんもしていたらしい。

[Java]考えなしに肥大化する定数クラス。- 谷本 心 in せろ部屋

java.io.ObjectStreamConstantsなど、Javaの中でこのアンチパターンが使われているのは良くネタにされる話。

3.7. インターフェースで定数定義


Effective Javaでは定数クラスよりenumを使いましょうと言っています。たしか。

Effective Java にのっている エレガントな Enum の使い方メモ - Futurismo

そもそもが定数クラスというのはtypesafeなenumが導入される以前の工夫だったため当然といえば当然な流れのように思います。

enumはJavaの5から導入されました。
この話の時は大体さくらばさんのこれが引用されます。

Typesafe Enum - J2SE 5.0 虎の穴


考えてみると定数だけが浮かび上がっているドメインていうのは不思議であり、
かなり限られているように思います。

本当ならばドメインのうちの「何かの属性の値」が定数という形で抽出されてしまっており、
ドメインから離れて固まっている可能性もある。

したがってJavaBeansなり、
前述したenumなりで正しくモデリングがなされているべきです。
でないと、定数クラスはどんどん肥大化してしまうでしょう。


当たり前の話ですが、例えば、
Colorクラスとか
Htmlクラスとか。
そういったモデリングであればそれに対しての属性だというのが明確になる。

例えば、定数であってもHogeNumberクラスのONEとHogeBookクラスのONEでは属しているクラスによって意味合いが違います。


ここまで話が飛躍すると段々身も蓋もなくなってきますが、
RDBMSなり、キャッシュ、インメモリDBとか、外部ファイル(xml、yml、properties)とか
そういうものを上手く使えば、定数クラスの役目は減るだろうと思います。

あとはフレームワークがちゃんと働けば、とか。


「いやいや、それでも男は黙って定数クラス!」とか

「そうは言っても、もうすでに定数クラスが山ほどあるんじゃい!」
とかそういうこともあると思います。

そういう場合は、これまでつらつらと書いたとおり、
なるべく定数クラスをドメインごとに分割する努力が必要です。

それでも、どうしても定数が膨大になってくることがあるかもしれません。特に巨大なシステムであったり、レガシーになってしまっていればなおさら。

そういう時はクラスの中にクラスを作って定数をグルーピングするのもありかなと思います。これどうなんだろうな。

public final class Constants {
    private Constants() {
    }
    public static class Money {
        public static final String YEN = "円";
        public static final String DOLLER = "ドル";
    }
    public static class Character {
        public static final String SLASH = "/";
        public static final String BLANK = " ";
        public static final String CRLF = "¥r¥n";
        public static final String LF = "¥n";
    }
}

こうすれば、こういう風にアクセス出来る。

System.out.println(Constants.Money.YEN);

これをやると

  • 親子関係が明確になるので意味が理解しやすくなる
  • コード補完で探しやすくなる
  • クラスの中でグルーピングされるので、ぱっと見で分かりやすくなる

とか良いこと?があるかなぁという気がする。

でも、これはあくまで最終手段というか、モデリングとかきちんとやったうえでこういうのも必要な時があればというか、そんな気持ち。

あと、単純にAのBという関係だけで表現出来ない時もあるんじゃないかなぁと思うんですよねぇ。

でもここまで頑張って定数クラス作る意味ってあるんかな。。




とまぁ、ぼんやり考えていることを書いてみましたが、答えはないです。
いやいやありえないだろ、、、とか色んな人からなんかしら意見が貰えると幸い。




このブログ記事から派生した話題






2 件のコメント:

  1. Javaの初心者です。
    昨日、授業で電卓をJavaで作りました。
    定数クラスが
    public final class Constant {

    public static final String TOTAL = "現在の値";
    public static final String SYMBOL = "演算子";
    public static final String INPUT ="入力された値";
    public static final String MSG = "を入力して下さい。>> ";
    public static final String NUM_SYMBOL = "数値又は演算子";
    public static final String NUM = "数値";
    public static final String EMPTY = "";
    public static final String ZERO = "0";
    public static final String ERROR1 = "エラー:演算子を入力して下さい。";
    public static final String ERROR2 = "エラー:値が1つしかありません。";
    public static final String ERROR3 = "エラー:0で割れません。";
    public static final String ERROR4 = "エラー:正しく入力して下さい。";
    public static final String ERROR5 = "エラー:数値を入力して下さい。";
    public static final String ERROR6 = "エラー:正しい数値や演算子を入力して下さい";
    public static final String ADD = "+";
    public static final String MINUS = "-";
    public static final String MULTIPLY = "*";
    public static final String DIVISION = "/";
    public static final String EQUAL = "=";
    public static final String C = "c";
    public static final String E = "e";
    public static final String CE = "ce";
    public static final String END = "終了します。";

    private Constant(){}
    }
    という感じになったのですが、実際会社に入ってプログラミングすると、こういう定数クラスは普通なのでしょうか?それとも「悪い例」なのでしょうか。ネットで軽く調べると、定数クラス自体出来るだけ使わない方が良いとかもチラホラ見かけました。private Constant(){}やpublic static finalの意味もよく分からないまま書いてますが、ずっとこの書き方のまま社会に出てプログラマーになっても大丈夫なのでしょうか?w

    返信削除
  2. もう自己解決しているかもしれませんが、念のため。

    この書き方は個人的には悪いと思っています。

    いちいちDBにアクセスして値持ってくるよりは早いのですし、楽なのですが。
    ただ入社後に会社の方針にあわせて直させるでしょうから、今は少なくとも問題ないでしょう。

    定数クラスの問題はあちこちで書かれているので皆さん気になっているようですが、
    業務上で経験した以下のような問題がありました。


     ・仕様書上決めるところではない場合、現場で独自のものが出てくる。
      客先に提出するDB設計書があれば問題ないが、定数クラスに関してはほぼ書かないので発生する。
      定数クラスAに書かれたresultCodeは1でOKの意味だけど、定数クラスBでは1はNGという意味など。
      単体テストは通せるかもしれないが、結合テストなどになると期待値が違うという問題になる。
     ・作っているシステムが大きい場合、
      『ここのディレクトリは取りまとめがあの人で、今は打ち合わせのため東京ではなく名古屋。』
      などもあって後回しにされてすぐには直せない。
      打ち合わせ中は連絡がすぐには取れず、最悪問題自体が忘れられて後工程で発覚。
      『言った言わない』や『なんで直さなかったんだ』などの問題になる。
     ・重複している内容の定数を使っているところが複数あった場合は参照先を直すだけで良さそうだが、
      『この問題に私は気が付いたのだけど、これらの変数名でどちらに寄せるか議論』が
      打ち合わせ中に始まると他のコーディングの手は止まる。
      『気が付いたけど打ち合わせで話すのとか面倒だし、自分で直しちゃおう。』というのは、
      少なくともチームかつJavaでシステムを作っている場合はまず通用しない。
      『新人が自己判断で修正した後発覚。会社間で大問題になる。』ということも割とある。

    返信削除