Javaアプリケーションの設定を保持する簡単な方法は、特定の値に関連付けられた各設定の識別子を含む「.properties」拡張子が付いたテキストファイルで表されます(この値は数値の場合があります) 、文字列、日付など。)C#も同様のアプローチを使用しますが、テキストファイルの名前は「App.config」にする必要があります。どちらの場合も、ソースコードで、設定を読み取るために特定のクラスを初期化する必要があります。このクラスには、指定された設定識別子に関連付けられた値(文字列)を返すメソッド。
// Java example
Properties config = new Properties();
config.load(...);
String valueStr = config.getProperty("listening-port");
// ...
// C# example
NameValueCollection setting = ConfigurationManager.AppSettings;
string valueStr = setting["listening-port"];
// ...
どちらの場合も、構成ファイルから読み込まれた文字列を解析し、変換された値を関連する型付きオブジェクトに割り当てる必要があります(この段階で解析エラーが発生する可能性があります)。解析ステップの後、設定値が特定の有効ドメインに属することを確認する必要があります。たとえば、キューの最大サイズは正の値である必要があり、一部の値は関連している可能性があります(例:最小<最大)、 等々。
アプリケーションが起動するとすぐに設定をロードする必要があるとします。つまり、アプリケーションが実行する最初の操作は設定をロードすることです。設定の無効な値は、自動的にデフォルト値に置き換える必要があります。これが関連する設定のグループで発生した場合、それらの設定はすべてデフォルト値で設定されます。
これらの操作を実行する最も簡単な方法は、最初にすべての設定を解析し、次に読み込まれた値をチェックし、最後にデフォルト値を設定するメソッドを作成することです。ただし、このアプローチを使用する場合、メンテナンスは困難です。アプリケーションの開発中に設定の数が増えると、コードの更新がますます難しくなります。
この問題を解決するために、以下のようにTemplate Methodパターンを使用することを考えました。
public abstract class Setting
{
protected abstract bool TryParseValues();
protected abstract bool CheckValues();
public abstract void SetDefaultValues();
/// <summary>
/// Template Method
/// </summary>
public bool TrySetValuesOrDefault()
{
if (!TryParseValues() || !CheckValues())
{
// parsing error or domain error
SetDefaultValues();
return false;
}
return true;
}
}
public class RangeSetting : Setting
{
private string minStr, maxStr;
private byte min, max;
public RangeSetting(string minStr, maxStr)
{
this.minStr = minStr;
this.maxStr = maxStr;
}
protected override bool TryParseValues()
{
return (byte.TryParse(minStr, out min)
&& byte.TryParse(maxStr, out max));
}
protected override bool CheckValues()
{
return (0 < min && min < max);
}
public override void SetDefaultValues()
{
min = 5;
max = 10;
}
}
問題は、この方法では、単一の値であっても、設定ごとに新しいクラスを作成する必要があることです。この種の問題に対する他の解決策はありますか?
要約すれば:
基本的に、外部設定ファイルはYAMLドキュメントとしてエンコードされます。これは、アプリケーションの起動時に解析され、構成オブジェクトにマップされます。
最終結果は堅牢で、何よりも管理が簡単です。
これを、構成値を取得するAPIとストレージ形式の2つの観点から検討してみましょう。それらはしばしば関連していますが、別々に検討することは役に立ちます。
構成API
テンプレートメソッドパターンは非常に一般的ですが、その一般性が本当に必要かどうかは疑問です。設定値のtypeごとにクラスが必要です。あなたは本当に多くの種類がありますか?文字列、int、float、booleans、およびenumのほんの一握りで十分であると思います。これらを前提として、いくつかのメソッドを持つConfig
クラスを作成できます。
int getInt(name, default, min, max)
float getFloat(name, default, min, max)
boolean getBoolean(name, default)
String getString(name, default)
<T extends Enum<T>> T getEnum(name, Class<T> enumClass, T default)
(私は私がその最後の1つで正しいジェネリックを得たと思います。)
基本的に、各メソッドは、設定ファイルからの文字列値の解析を処理する方法と、エラーを処理し、必要に応じてデフォルト値を返す方法を知っています。おそらく数値の範囲チェックで十分です。範囲値を省略したオーバーロードが必要な場合があります。これは、Integer.MIN_VALUE、Integer.MAX_VALUEの範囲を提供することと同じです。 Enumは、文字列の固定セットに対して文字列を検証するタイプセーフな方法です。
複数の値、相互に関連する値、動的テーブルルックアップなど、これが処理しないものがあります。これらのために特別な解析および検証ルーチンを書くことができますが、これが複雑すぎる場合は、質問し始めますあなたが設定ファイルを使いすぎているかどうか。
ストレージ形式
Javaプロパティファイルは、個々のキーと値のペアを格納するのに適しているように見え、上で説明した値のタイプをかなりサポートしています。 XMLやJSONなどの他の形式を検討することもできますが、データをネストしたり繰り返したりしない限り、これらはおそらくやりすぎです。その時点では、設定ファイルをはるかに超えているようです...
Telastynはシリアル化されたオブジェクトについて言及しました。これは可能性ですが、シリアライゼーションには困難があります。これはテキストではなくバイナリであるため、値を確認および編集することは困難です。シリアル化の互換性に対処する必要があります。シリアル化された入力から値が欠落している場合(たとえば、フィールドをConfigクラスに追加し、その古いシリアル化された形式を読み取っている場合)、新しいフィールドはnull /ゼロに初期化されます。他のデフォルト値を入力するかどうかを決定するロジックを記述する必要があります。しかし、ゼロは構成値がないことを示していますか、それともゼロに指定されていましたか?次に、このロジックをデバッグする必要があります。最後に(これが問題かどうかは不明ですが)、シリアル化されたオブジェクトストリームの値を検証する必要がある場合があります。悪意のあるユーザーがシリアル化されたオブジェクトストリームを検出不能に変更することは(不便ですが)可能です。
可能であれば、プロパティにこだわると思います。
私がそれをした方法:
すべてをデフォルト値に初期化します。
ファイルを解析し、値を保存します。設定される場所は、値が受け入れ可能であることを確認する責任があります。不正な値は無視されます(したがって、デフォルト値が保持されます)。
この種の問題に対する他の解決策はありますか?
必要なのが単純な構成である場合は、単純な古いクラスを作成します。これはデフォルトを初期化し、アプリが組み込みのシリアル化クラスを介してファイルからロードできます。次に、アプリはそれを必要なものに渡します。解析や変換をいじったり、設定文字列をいじったり、ガベージをキャストしたりする必要はありません。また、設定wayをサーバーから、またはプリセットとして保存/ロードする必要があるコード内のシナリオで使いやすくなり、wayユニットで使いやすくなりますテスト。
少なくとも.NETでは、独自の強く型付けされた構成オブジェクトをかなり簡単に作成できます。簡単な例については、これを参照してください MSDNの記事 。
Protip:構成クラスをインターフェースでラップし、アプリケーションにそれを通信させます。テストまたは利益のために偽の構成を簡単に注入できます。