我的代码中是否更喜欢可读性而不是安全性?

时间:2015-03-02 19:21:27

标签: java readability

我正在处理一些具有创建Map实例的一致模式的代码:

Map<String, String> valuesMap = new HashMap<String, String>();
valuesMap.put("UserName", "Value1");
valuesMap.put("FirstName", "Value2");
valuesMap.put("LastName", "Value3");
valuesMap.put("Email", "Value4");

我认为它可以像这样读取:

Map<String, String> valuesMap = createMap(
    "UserName", "Value1",
    "FirstName", "Value2",
    "LastName", "Value3",
    "Email", "Value4"
);            

借助以下方法:

private Map<String, String> createMap(String... parameters) {
    Map<String, String> valueMap = new HashMap<String, String>();
    if (parameters.length % 2 == 0) {
        for (int i = 0; i < parameters.length; i+=2){
            String key = parameters[i];
            String value = parameters[i + 1];
            valueMap.put(key, value);
        }
    } else {
        throw new IllegalArgumentException("The parameters to this method must be even in number");
    }
    return valueMap;
}

但是通过这样做,我失去了在编译时捕获错误的能力。 例如:有人可以轻松地执行以下操作

Map<String, String> valuesMap = createMap(
    "UserName", "Value1",
    "FirstName", "Value2",
    "LastName", // missing value for key
    "Email", "Value4"
);            

我很想使用更易读的方法,需要你们的建议。

编辑:

  • 有许多实例被宣布为第一个例子。
  • 不填充键和值,但更像是静态初始化器的String decalration。
  • 它更像是包含多个声明的代码
  • 每个声明的键和值都不同

---- @Jon Skeet感谢您指出错误。

5 个答案:

答案 0 :(得分:4)

尽管有各种各样的评论,我看到这样的辅助方法使用了很多,用于静态初始化,并且通常发现它们比替代方法更简单。当然,它仅在您的键和值类型相同时才有效 - 否则您可以为(例如)最多4个键和4个值提供重载,此时您也可以获得编译时安全性。

您实际上并没有跳过安全性 - 您将编译时安全性推迟到执行时安全性。只要你至少有一个执行该代码的单元测试,你就会发现错误(当你抛出异常时)。根据我的经验,像这样的地图通常在测试中使用,或者作为永不变异的静态最终地图。 (对于后者,考虑使用不可变映射来强制执行缺少变异......)

那就是说,我会重构方法来首先验证计数:

if (parameters.length % 2 != 0) {
    throw new IllegalArgumentException("Odd number of keys and values provided");
}
// Now we can just proceed
Map<String, String> valueMap = new HashMap<String, String>();
for (int i = 0; i < parameters.length; i+=2) {
    String key = parameters[i];
    String value = parameters[i + 1];
    valueMap.put(key, value);
}
return valueMap;

请注意,由于您的循环边界检查,之前您的填充代码中存在错误。

答案 1 :(得分:4)

我敦促你考虑这样做:

@SafeVarargs
public static <K,V> HashMap<K,V> hashMap(Map.Entry<K,V>... kvs) {
  return map(HashMap::new, kvs);
}

@SafeVarargs
public static <M extends Map<K, V>, K, V> M map(
    Supplier<? extends M> supplier, Map.Entry<K, V>... kvs)
{
  M m = supplier.get();
  for (Map.Entry<K,V> kv : kvs) m.put(kv.getKey(), kv.getValue());
  return m;
}

public static <K,V> Map.Entry<K,V> kv(K k, V v) { 
  return new SimpleImmutableEntry<K, V>(k,v); 
}

您的客户端代码如下所示:

Map<Integer, String> map = hashMap(kv(1, "a"), kv(2, "b"));

并且它将是100%类型安全的,因为键值对在静态类型级别表示。

BTW这并不是从根本上依赖于Java 8的功能。这是Java 7的简化版本:

@SafeVarargs
public static <K,V> HashMap<K,V> hashMap(Map.Entry<K,V>... kvs) {
  HashMap<K, V> m = new HashMap<>();
  for (Map.Entry<K, V> kv : kvs) m.put(kv.getKey(), kv.getValue());
  return m;
}

答案 2 :(得分:2)

采用相同类型的长参数列表的方法被认为是不好的做法。

在您的情况下,而不是使用包含您的用户的详细信息的地图。考虑使用成员userName,firstName,lastName和email创建一个类用户。

答案 3 :(得分:2)

如果你考虑一下,可读性和安全性是高度相关的。极其可读的代码是如此透明,任何畸形都会在您的想法表达简单的背景下闪耀。非常安全的代码必须是可读的,因为通过使代码审查变得困难,您最终会在后续维护期间完成错误。

这里没有冲突可以平衡。你要做的任何事情都可以使帮助方法的实现更安全,这将使它的使用更具可读性。

例如,它不必采用字符串,它可以采用成对的字符串(辅助类)。或者它可以检查其输入的健全性。或者,您可以使用问题中显示的put方法。任何这些替代方案都可以使结果更安全更具可读性。

“可读”意味着每次看到它时都不必仔细检查模式,对吗?

答案 4 :(得分:1)

这是一个好主意 - 实际上很棒,但你必须小心错误。在您的情况下,如果有奇数条目,您需要大声抱怨。我经常通过尽可能防止程序启动来做到这一点,除非数据被外部化,此时你只想大声抱怨并可能恢复到以前的值,以免破坏生产服务器坏数据。

如果您可以识别名称与&#34;值&#34;你也应该这样做。

我做了很多这样的事情 - 只要记住,无论何时使用这样的代码删除编译时错误检查,你都要承担运行时的责任!

顺便说一句,你还可以进一步改进错误检查和可读性(使用的逗号只是一个建议,你只需选择一些不能进入任何字段的分隔符,逗号,冒号和tildie都是不错的选择):

String[] values=new String[]{
    "Name1,Value1",
    "Name2,Value2",
    ...
}

Then to parse:

for(String s:values) {
    String[] splitted=s.split(",")
    assert(splitted.size == 2)
    valuesMap.put(splitted[0],splitted[1])
}

我不断地使用这个模式和字符串数组初始化。您可以包含诸如方法名称之类的东西,以便将数据绑定到反射代码。

这样做的另一个好处是,外部化很容易使运行时配置和外部化成为可能,几乎不需要额外的工作。