ネストされたパブリッククラスを使用して定数を整理する


21

私は多くの定数を持つアプリケーションに取り組んでいます。前回のコードレビューで、定数が分散しすぎているため、すべてを単一の「マスター」定数ファイルに整理する必要があることがわかりました。意見の相違は、それらを整理する方法についてです。大多数は、定数名を使用することで十分であると感じていますが、これは次のようなコードにつながります。

public static final String CREDITCARD_ACTION_SUBMITDATA = "6767";
public static final String CREDITCARD_UIFIELDID_CARDHOLDER_NAME = "3959854";
public static final String CREDITCARD_UIFIELDID_EXPIRY_MONTH = "3524";
public static final String CREDITCARD_UIFIELDID_ACCOUNT_ID = "3524";
...
public static final String BANKPAYMENT_UIFIELDID_ACCOUNT_ID = "9987";

このタイプの命名規則は扱いにくいと思います。ネストされたパブリッククラスを使用する方が簡単だと思い、次のようなものがあります。

public class IntegrationSystemConstants
{
    public class CreditCard
    {
        public static final String UI_EXPIRY_MONTH = "3524";
        public static final String UI_ACCOUNT_ID = "3524";
        ...
    }
    public class BankAccount
    {
        public static final String UI_ACCOUNT_ID = "9987";
        ...
    }
}

このアイデアは「複雑すぎた」ため、あまり受け入れられませんでした(なぜこれが複雑すぎるのについてはあまり詳しく知りませんでした)。これにより、関連する定数のグループ間のより良い分割が作成され、オートコンプリートによりこれらの定数も見つけやすくなると思います。しかし、私はこれが行われたことを見たことがないので、これが受け入れられている慣行なのか、それを行わないほうがよい理由があるのか​​疑問に思っています。


1
むしろそのアイデアが好きです。C#スタイルの列挙型のスコープと動作を望んでいたときに、C ++で似たようなことをしていましたが、意味のある値を使用していました(Scalaで列挙型の代わりにケースクラスを使用することに慣れていたため、アイデアが生まれたと思います)。確かに、これは楽しみのための個人的なプロジェクトであり、チームの努力ではありませんでした。
KChaloux

回答:


13

2つの提案のどちらにも同意しません。

定数は、提案されている2つの形式のいずれかの全定数クラスではなく適切なクラスにある必要があります。

定数のみのクラス/インターフェースがあってはなりません。

クラスCreditCard(内部クラスではない)が存在する必要があります。このクラス/インターフェースには、クレジットカードに関連するメソッドと、定数UI_EXPIRY_MONTHおよびがありUI_ACCOUNT_IDます。

クラス/インターフェースが存在する必要がありますBankAccount(内部クラスではありません)。このクラス/インターフェイスには、銀行口座に関連するメソッドとconstantがありUI_ACCOUNT_IDます。

たとえば、Java APIでは、すべてのクラス/インターフェースに定数があります。これらは、内部クラスにグループ化されているかどうかに関係なく、数百の定数を持つConstantsクラス/インターフェイスにはありません。

たとえば、結果セットに関連するこれらの定数はインターフェイスにありますResultSet

static int  CLOSE_CURSORS_AT_COMMIT 
static int  CONCUR_READ_ONLY 
static int  CONCUR_UPDATABLE 
static int  FETCH_FORWARD 
static int  FETCH_REVERSE 
static int  FETCH_UNKNOWN 
static int  HOLD_CURSORS_OVER_COMMIT 
static int  TYPE_FORWARD_ONLY 
static int  TYPE_SCROLL_INSENSITIVE 
static int  TYPE_SCROLL_SENSITIVE 

このインターフェイスには、結果セットに関連するメソッドシグネチャがあり、実装クラスはその動作を実装します。彼らは単なる定番ではありません。

UIアクションに関連するこれらの定数は、インターフェースにありますjavax.swing.Action

static String   ACCELERATOR_KEY 
static String   ACTION_COMMAND_KEY 
static String   DEFAULT 
static String   LONG_DESCRIPTION 
static String   MNEMONIC_KEY 
static String   NAME 
static String   SHORT_DESCRIPTION 
static String   SMALL_ICON 

実装クラスは、UIアクションに関連する動作を持ち、単なる定数の保持者ではありません。

SwingConstantsメソッドを持たない少なくとも1つの「定数」インターフェース()を知っていますが、何百もの定数はなく、ごく少数です。これらはすべて、UI要素の方向と位置に関連しています。

static int  BOTTOM 
static int  CENTER 
static int  EAST 
static int  HORIZONTAL 
static int  LEADING 
static int  LEFT 
static int  NEXT 
static int  NORTH 
static int  NORTH_EAST 
static int  NORTH_WEST 
static int  PREVIOUS 
static int  RIGHT 
static int  SOUTH 
static int  SOUTH_EAST 
static int  SOUTH_WEST 
static int  TOP 
static int  TRAILING 
static int  VERTICAL 
static int  WEST 

定数のみのクラスはオブジェクト指向の設計として適切ではないと思います。


1
ほとんどの場合、定数のみのクラスは設計の誤りの兆候であることに同意しますが、正当な用途があります。たとえば、定数は特定のクラスに常に「属する」わけではありません。AndroidプログラミングのIntentエキストラのキーは、そのような具体例の1つ​​です。
curioustechizen

1
+1ですが、UI定数はおそらくBankAccountのようなビジネスモデルにはないはずです。それらはいくつかのUIクラスに属します。
ケビンクライン14

10

定数が分散しすぎているため、すべてを単一の「マスター」定数ファイルに編成する必要があることがわかりました。

これは、定数が「見つけるのが難しい」という問題に対する100%間違った解決策です。定数、列挙型、インターフェイスなどの技術的な基準で物事をグループ化することは、基本的なエラーです(残念ながらプログラマーによって行われます)。

レイヤアーキテクチャを除き、物事はドメインごとにグループ化する必要があり、定数はさらに非常に低レベルの要素なのでグループ化する必要があります。定数は、APIの一部として使用するクラスまたはインターフェイスの一部である必要があります。彼らが住むための自然な場所が見つからない場合は、APIデザインをクリーンアップする必要があります。

さらに、1つの巨大な定数ファイルは、コンパイル時に定数を使用するクラスにインライン化されるため、Javaでの開発速度を低下させます。したがって、すべての変更により、これらすべてのファイルおよびそれらに依存するすべての再コンパイルが強制されます。どこでも使用される定数を含むファイルが1つある場合、インクリメンタルコンパイルの利点はほとんど失われます。Eclipseがそのファイルを変更するたびにプロジェクト全体を再コンパイルするため、15分間ロックされるのを確認してください。また、そのファイルにはどこでも使用されるすべての定数が含まれているため、頻繁に変更することになります。はい、私は個人的な経験から話しています。


開発環境への潜在的な影響については知りませんでしたが、ネストされたクラスのアプローチはそれであまり役に立たないでしょうね?
FrustratedWithFormsDesigner

1
@FrustratedWithFormsDesigner:インクリメンタルコンパイルメカニズムが依存関係のモデリングにどれだけの労力を費やしているかによって異なります。
マイケルボルグ

9

あなたが正しいです。あなたの提案により、プログラマーimport static Constants.BankAccount.*は 'BANKACCOUNT_'の無数の繰り返しを排除できます。連結は、実際のスコープの貧弱な代替です。とは言うものの、チームカルチャーを変えることを期待することはあまりありません。彼らには適切な慣行の概念があり、間違っていると言う100人の専門家を見せても変わらないでしょう。

また、なぜあなたは単一の「定数」ファイルを持っているのか疑問に思っています。これらの定数が何らかの形で関連していて非常に安定していない限り、これは非常に悪い習慣です。より典型的には、常に変化し、すべてに依存する一種のキッチンシンククラスになります。


現在、ほとんどのUIフィールドID定数を含む1つの定数ファイルと、サブプロジェクト固有のその他のいくつかの定数ファイルがあります。しかし、今では、サブプロジェクト固有の定数を他の関連サブプロジェクトで使用する必要があるため、それらをすべて1つの「マスター」定数ファイルにマージするようになりました。これは、どの定数ファイルが値を参照するかわからないこともあり、別のサブプロジェクトの定数ファイルで見つからなかったために誰かが定数の複製を作成したためです。
FrustratedWithFormsDesigner

8
絶えず変化している定数クラス...十分に掘り下げれば、そこには何か禅があります。
KChaloux

2
@FrustratedWithFormsDesigner:UIフィールドID?値を見つける場所がわからないのですか?WTFの巨大な山のように聞こえます。あなたは私の同情を持っています。
ケビンクライン

6

両方のアプローチが機能し、それが一日の終わりに重要なことです。

とはいえ、あなたのアプローチはパターンと見なされるほど一般的であり、より正確には、一定のインターフェースのアンチパターンに対する十分な改善です。何が複雑なのかわかりませんが、それはあなたのチームとの話し合いです。


十分に長い定数のリストについては、すべてを同じクラスに含めるよりも、定数のインターフェースを好むでしょう。オートコンプリートから間違ったものを選択することは危険なことです。もちろん、それはどれくらいの長さであるかに依存します:)
ゴランジョヴィック

1
@GoranJovicまあ、すべてのアンチパターンは何らかの方法で(または少なくとも便利な方法で)有用であり、そうでなければパターンになりません。
ヤニス

1

定数の長いリストの課題の1つは、使用する「正しい」定数を見つけることです。常に、誰かが定数を、所属するグループに追加する代わりに、行の最後に追加します。

それはあなたのチームと議論するための別のポイントをもたらします-すでに名前による定数の事実上の分類があります。ですから、彼らにとって現在の状態からあなたが提案しているものへの大きなシフトであるべきではありません。オートコンプリートを使用すると、一定の検索がさらに簡単になります。

どちらかといえば、あなたの提案は、それが特定の状況に適合するかもしれないとしても、「間違った」定数の使用を思いとどまらせるのに役立ちます。代表クラス内で定数をラップすることにより、開発者が定数を使用する必要があるかどうかを検討するようになります。たとえばCreditCard.SomeConstant、一般的な使用法を考えてみると、なぜGeneral.SomeConstantそのような状況ではないのか、本当に疑問に思うはずです。

私は定数のモンスターの量でプロジェクトに行ってきました、そして、私はあなたが提案している方法を持つことを好んだでしょう。よりクリーンで、より直接的で、不注意な使用を避けるのに役立ちます。


1

特に、よく知られた固定されたXML構造、または同様にネストされた文字列が重い...たとえばカスタム構成ブロックパーサーに対してプログラム/解析する必要がある場合、私はこれを時々(C#で)行います。

XMLの階層構造と一致するネストされたクラス構造を構築します。クラス名は、const文字列「ElementName」プロパティと各属性(存在する場合)に対応する同様のプロパティを含む要素に対応します。

対応するXmlに対して非常に簡単にプログラミングできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.