私の経験では、この種の問題は、より深い問題を隠しています: 実際の OOP を実行できず、DRY 原則に従わないことです。
簡単に言えば、ステートメント内の各アクションの適切な定義によって起動時の決定を取得し、実行時テストと実行時テストの両方を破棄します。if
config_options
詳細は以下。
サンプルの使用法は次のとおりです。
if (config_options.value('FOO_ENABLED') == 'Y') ...
特に次のステートメントを考えると、「省略記号で何が起こっているのか?」という明白な疑問が生じます。
(もちろん、この同じオプションは、システム コードの多くの場所でチェックする必要があるかもしれません。)
config_option
これらの値のそれぞれが、実際には 1 つの問題領域 (または実装戦略) の概念に対応していると仮定しましょう。
これを行う代わりに (コード全体のさまざまな場所で繰り返し):
- 文字列(タグ)を取得し、
- 対応する他の文字列 (値) を見つけ、
- その値をブール値に相当するものとしてテストし、
- そのテストに基づいて、何らかのアクションを実行するかどうかを決定します。
「構成可能なアクション」の概念をカプセル化することをお勧めします。
FOO_ENABLED
コードが英単位またはメートル単位のいずれかで機能する必要があることを例に取りましょう (明らかに ... と同じくらい仮説的です ;-)。が「true」の場合METRIC_ENABLED
、内部計算のためにユーザーが入力したデータをメートル法から英語に変換し、結果を表示する前に元に戻します。
インターフェイスを定義します。
public interface MetricConverter {
double toInches(double length);
double toCentimeters(double length);
double toPounds(double weight);
double toKilograms(double weight);
}
の概念に関連するすべての動作を 1 か所で識別しMETRIC_ENABLED
ます。
次に、これらの動作が実行されるすべての方法の具体的な実装を記述します。
public class NullConv implements MetricConverter {
double toInches(double length) {return length;}
double toCentimeters(double length) {return length;}
double toPounds(double weight) {return weight;}
double toKilograms(double weight) {return weight;}
}
と
// lame implementation, just for illustration!!!!
public class MetricConv implements MetricConverter {
public static final double LBS_PER_KG = 2.2D;
public static final double CM_PER_IN = 2.54D
double toInches(double length) {return length * CM_PER_IN;}
double toCentimeters(double length) {return length / CM_PER_IN;}
double toPounds(double weight) {return weight * LBS_PER_KG;}
double toKilograms(double weight) {return weight / LBS_PER_KG;}
}
config_options
起動時に、一連の値をロードする代わりに、構成可能な一連のアクションを次のように初期化します。
MetricConverter converter = (metricOption()) ? new MetricConv() : new NullConv();
(metricOption()
上記の式は、METRIC_ENABLED の値を調べるなど、必要な 1 回限りのチェックの代用です ;-)
次に、コードが言うところはどこでも:
double length = getLengthFromGui();
if (config_options.value('METRIC_ENABLED') == 'Y') {
length = length / 2.54D;
}
// do some computation to produce result
// ...
if (config_options.value('METRIC_ENABLED') == 'Y') {
result = result * 2.54D;
}
displayResultingLengthOnGui(result);
次のように書き換えます。
double length = converter.toInches(getLengthFromGui());
// do some computation to produce result
// ...
displayResultingLengthOnGui(converter.toCentimeters(result));
その 1 つの概念に関連するすべての実装の詳細がきれいにパッケージ化されているため、関連する将来のすべてのメンテナンスMETRIC_ENABLED
を 1 か所で行うことができます。さらに、実行時のトレードオフは有利です。メソッドを呼び出す「オーバーヘッド」は、Map から String 値を取得して String#equals を実行するオーバーヘッドと比較すると些細なものです。