以我的经验,这类问题掩盖了一个更深层次的问题:未能进行实际的 OOP 并遵循 DRY 原则。
简而言之,在启动时通过语句中每个操作的适当定义来捕获决策,然后if
丢弃config_options
运行时测试和运行时测试。
详情如下。
示例用法是:
if (config_options.value('FOO_ENABLED') == 'Y') ...
这提出了一个明显的问题,“省略号中发生了什么?”,特别是考虑到以下陈述:
(当然,同样的选项可能需要在系统代码中的很多地方进行检查。)
让我们假设这些config_option
值中的每一个都确实对应于单个问题域(或实施策略)概念。
而不是这样做(重复,在整个代码的各个地方):
- 取一个字符串(标签),
- 找到它对应的其他字符串(值),
- 将该值测试为布尔等效项,
- 根据该测试,决定是否执行某些操作。
我建议封装“可配置操作”的概念。
FOO_ENABLED
让我们以您的代码必须以英制单位或公制单位工作为例(显然就像... ;-) 一样假设。如果METRIC_ENABLED
为“真”,则将用户输入的数据从公制转换为英制以进行内部计算,并在显示结果之前转换回来。
定义一个接口:
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
都可以在一个地方完成。此外,运行时权衡是一种胜利;与从 Map 中获取 String 值并执行 String#equals 的开销相比,调用方法的“开销”微不足道。