По моему опыту, проблема такого рода маскирует более глубокую проблему: неспособность выполнить ООП и следовать принципу СУХОЙ.
В двух словах, запишите решение во время запуска соответствующим определением для каждого действия внутри операторов if
, а затем отбросьте как config_options
, так и тесты времени выполнения. 1007 *
Подробности ниже.
Пример использования:
if (config_options.value('FOO_ENABLED') == 'Y') ...
, который поднимает очевидный вопрос: «Что происходит в многоточии?», Особенно с учетом следующего утверждения:
(Конечно, эту же опцию, возможно, нужно проверять во многих местах системного кода.)
Предположим, что каждое из этих config_option
значений действительно соответствует концепции одной проблемной области (или стратегии реализации).
Вместо этого (неоднократно, в разных местах кода):
- Взять строку (тег),
- Найдите соответствующую ему другую строку (значение),
- Проверьте это значение как логический эквивалент,
- На основании этого теста решить, следует ли выполнить какое-либо действие.
Я предлагаю инкапсулировать концепцию «настраиваемого действия».
Давайте возьмем в качестве примера (очевидно, такой же гипетический, как FOO_ENABLED
... ;-), что ваш код должен работать в английских или метрических единицах. Если METRIC_ENABLED
равно «true», преобразуйте введенные пользователем данные из метрики в английский для внутренних вычислений и выполните обратное преобразование до отображения результатов.
Определить интерфейс:
public interface MetricConverter {
double toInches(double length);
double toCentimeters(double length);
double toPounds(double weight);
double toKilograms(double weight);
}
, который идентифицирует в одном месте все поведение, связанное с понятием 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; -)
Тогда везде, где код сказал бы:
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));
Поскольку все детали реализации, относящиеся к этой единой концепции, теперь аккуратно упакованы, все последующее обслуживание, связанное с METRIC_ENABLED
, может выполняться в одном месте. Кроме того, компромисс во время выполнения - это победа; «накладные расходы» на вызов метода тривиальны по сравнению с накладными расходами на извлечение значения String из Map и выполнение String # equals.