根据我的经验,这类问题掩盖了一个更深层次的问题:未能进行实际的 OOP 并遵循 DRY 原则。
简而言之,通过在if 语句内部 中的每个操作的适当定义捕获启动时的决定@ 语句,然后丢弃config_options 和运行时测试。
详情如下。
示例用法为:
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 相关的所有未来维护都可以在一个地方完成。此外,运行时权衡是一种胜利;与从 Map 中获取 String 值并执行 String#equals 的开销相比,调用方法的“开销”微不足道。