代码审查后,我刚刚讨论了设计选择。我想知道您的意见是什么。
有一个此类Preferences
,它是键-值对的存储桶。空值是合法的(这很重要)。我们希望某些值可能尚未保存,并且我们希望通过在请求时使用预定义的默认值对其进行初始化来自动处理这些情况。
讨论的解决方案使用以下模式(注意:显然,这不是实际的代码-出于说明目的而进行了简化):
public class Preferences {
// null values are legal
private Map<String, String> valuesFromDatabase;
private static Map<String, String> defaultValues;
class KeyNotFoundException extends Exception {
}
public String getByKey(String key) {
try {
return getValueByKey(key);
} catch (KeyNotFoundException e) {
String defaultValue = defaultValues.get(key);
valuesFromDatabase.put(key, defaultvalue);
return defaultValue;
}
}
private String getValueByKey(String key) throws KeyNotFoundException {
if (valuesFromDatabase.containsKey(key)) {
return valuesFromDatabase.get(key);
} else {
throw new KeyNotFoundException();
}
}
}
它被批评为控制流的反模式滥用异常。KeyNotFoundException
-仅针对该用例而生-永远不会超出此类范围。
从本质上讲,这是两种使用fetch进行获取信息的方法。
数据库中不存在的密钥并不令人担忧或例外-我们希望每当添加新的首选项设置时都会发生这种情况,因此,如果需要,可以使用默认值优雅地对其进行初始化的机制。
与此相反的是getValueByKey
-私有方法-因为现在定义具有告知公众对方法没有自然的方式,这两种价值,关键是否在那里。(如果不是,则必须添加它,以便可以更新值)。
返回值null
是模棱两可的,因为这null
是完全合法的值,因此无法确定它是否意味着密钥不存在,或者是否存在null
。
getValueByKey
将必须返回某种a Tuple<Boolean, String>
,如果密钥已经存在,则将bool设置为true,以便我们可以区分(true, null)
和(false, null)
。(out
可以在C#中使用参数,但这是Java)。
这是更好的选择吗?是的,您必须定义一些一次性使用的类以达到的效果Tuple<Boolean, String>
,但随后我们将摆脱KeyNotFoundException
,从而实现了这种平衡。我们也避免了处理异常的开销,尽管它在实际意义上并不重要-无需考虑性能方面的考虑,它是客户端应用程序,也不像每秒会检索用户偏好数百万次。
这种方法的一种变体可以使用Guava Optional<String>
(在整个项目中已经使用了Guava)而不是一些自定义方法Tuple<Boolean, String>
,然后我们就可以区分Optional.<String>absent()
“适当”了null
。但是,由于易于理解的原因,它仍然让人感到有些呆滞-引入两个“空”级别似乎首先滥用了创建Optional
s 背后的概念。
另一个选择是显式检查密钥是否存在(添加boolean containsKey(String key)
方法并getValueByKey
仅在我们已经断言它存在的情况下调用)。
最后,也可以内联私有方法,但是实际方法getByKey
比我的代码示例复杂一些,因此内联会使它看起来很讨厌。
我可能会在这里分开,但我很好奇在这种情况下您会押宝于最佳实践。我在Oracle或Google的风格指南中找不到答案。
使用代码示例中的异常是一种反模式,还是因为替代方法不是很干净?如果是,在什么情况下可以?反之亦然?
getValueByKey
公开场合也会变得更加有趣。