web-dev-qa-db-ja.com

コンストラクターのパラメーターではないプロパティの初期化:代替

クラスはファイルからプロパティをロードし、その役割を実行するときにそれらを使用します。テストのために、テストプロパティファイルを使用せずに、上記のプロパティを直接変更できるようにしたいと考えています。私は実装をしましたが、それはにおいで満たされているようです-私はこれを小さく保つことに取り組んでいます(プロパティをラップして注入するSpringのようなフレームワークを避けています)。

特に、静的に読み込まれた一連のプロパティを介して自身を初期化するコンストラクターを目にしたことは思い出せません。特に、渡されたパラメーター以外の場所から変数値を設定します。

私はこのアプローチに対する/反対の議論を探しており、これが本当に私が思うほどクールでない場合は、いくつかの代替案を見つけたいと思っています。

private static final String CONFIG_FILE_NAME = String.format("config-%s.properties",
        null != System.getProperty("Java_ENV") ? System.getProperty("Java_ENV") : "development");
static {
    PROPERTIES = new Properties();
    try {
        PROPERTIES.load(MyClass.class.getClassLoader().getResourceAsStream(CONFIG_FILE_NAME));
        if (PROPERTIES.keySet().size() < 1) {
            LOGGER.error(String.format("failed to load %s: no properties", CONFIG_FILE_NAME));
        }
    }
    catch (final IOException ie) {
        LOGGER.error(String.format("failed to load %s", CONFIG_FILE_NAME), ie);
    }
}

private String property1;
private String property2;

public MyClass() {
    property1 = PROPERTIES.getProperty("prop.1");
    property2 = PROPERTIES.getProperty("prop.2");
}

/* public accessors for property1 and property2 go here */
3
eebbesen

まず、名前付きクラスまたはインターフェースの静的イニシャライザは、チェック済み例外をスローできません( JLS 11.2.3。Exception Checking )。つまり、アプリケーションの設計に初期化時のさまざまな問題のチェック例外が何らかの形で含まれている場合、静的初期化子は単にオプションではありません。

コードスニペットを考えると、すでにこれに気づいています。catchブロックは、再スローせずにIOExceptionをログに記録するだけです。しかし、チェックされた例外がなくても、これはかなり面倒な方法です。これをテストするには、catchブロックからランタイム例外を再スローするコードを追加し(これはコンパイルされます)、構成ファイルが存在しないことをシミュレートします。

アプリケーションの保守担当者が、構成ファイル名のルーチンのタイプミスについて、あいまいな ExceptionInInitializerError を報告するログを受け取ったときの感想を考えてください。

この方法が便利でないもう1つの理由は、 JLS 12.4.1にあります。初期化が発生した場合

...クラスClassおよびパッケージJava.lang.reflectの特定のリフレクトメソッドを呼び出すと、クラスまたはインターフェイスが初期化されます...

上記は、初期化が発生したときに多くの制御を放棄することを意味します。一部のフレームワークでは、予期していなくてもトリガーされる場合があります。メモリが機能する場合、私の実践では、ロギングがまだ完全に初期化されていないときに一度発生しました。ログファイルが空のときにアプリケーションがクラッシュする理由を調査するのは面白かったです。


最後になりましたが、このデザインは単体テストの目的には非常に不便です。単体テストでMyClassを使用する場合は、必ず1)ファイルシステムに構成ファイルが存在し、2)目的の値とプロパティが含まれていることを確認する必要があります。

このデザインを、単にプロパティをパラメーターとして取るコンストラクターと比較します。

    MyClass(Properties properties) {
        this.properties = properties; // initialize field from constructor parameter
    }

上記のように構築されたクラスを使用したテストは、簡単です。ファイルシステムをいじる必要はありません、必要なプロパティと値をファイルに入力する手間はありません、単純なJavaコード。 Properties オブジェクトを作成してそれを詰め込みますただし、テストコードから直接、どこにいても、都合のよいときにいつでも使用できます。

要約すると、静的に読み込まれた一連のプロパティを介してそれ自体を初期化するコンストラクタを見て思い出せない理由がたくさんあります

4
gnat