2015-03-02 7 views
4

Я имею дело с каким-то кодом, который имеет последовательную структуру создания экземпляров Карты, как это:Должен ли я предпочитаю читаемость по безопасности в моем коде?

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" 
);    

Я соблазном использовать более читаемый метод, нужны предложения от вас, ребят.

Edit:

  • Есть много случаев, объявленных как в первом примере.
  • Ключи и значения не заполнены, а являются декаляцией строк больше, чем статические интинерализаторы.
  • больше как код, содержащий несколько объявлений
  • ключи и значения различны на каждом declareation

---- Скит @ Джон Спасибо за указание на ошибку.

+3

'createMap()' метод - это ужасный способ заселения карты. Избегай это. Может быть другой способ улучшить его, если вы сообщите нам, откуда вы читаете эти значения? –

+2

Почему бы не сделать * лучший * метод, который является удобочитаемым * и * безопасным? –

+0

значения - это укусы, как показано выше в коде, я не заселяю их нигде. – VishalDevgire

ответ

2

Метод, который принимает длинный список параметров одного и того же типа, считается плохой практикой.

В вашем случае вместо использования Карты, содержащей данные для вашего пользователя. Подумайте о создании пользователя класса с членами userName, firstName, lastName и электронной почтой.

+0

Да, я могу это сделать, но я хочу пропустить создание небольших небольших классов, которые используются только один раз. – VishalDevgire

+0

Почему вы хотите пропустить создание небольших классов? – bhspencer

+1

Большая часть внутреннего дизайна java - это оптимизация создания и сбора небольших классов. Если вы не создаете огромную кучу (тысячи), я бы очень рекомендовал его. Также наличие этих небольших классов дает вам подходящее место для кода, который относится к этим данным. Если у вас есть метод validateUserName(), то он будет принадлежать этому объекту, а его размещение в другом месте будет плохим дизайнерским решением. Не создавать классы, кажется, главная причина плохого дизайна OO, с которым я столкнулся. –

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; 

Заметьте, что вы перед ошибкой в ​​вашем демографическом коде произошла ошибка из-за проверки границ вашего цикла.

+0

Вы только что составили фразу «безопасность исполнения»;) – bhspencer

+1

@bhspencer: О, нет, не то, что я знаю. Некоторые могут назвать это «безопасностью во время выполнения», но я предпочитаю использовать «время выполнения», чтобы различать «время, в которое выполняются действия», и «вещь, которая запускает код» (например, VM). –

+0

Я googled для «безопасности исполнения времени» и не получил никаких результатов. Я также пытаюсь найти хорошее определение для «безопасности во время выполнения», есть ли у вас ссылка на определение? – bhspencer

1

Это хорошая идея - на самом деле здорово, но вы должны быть осторожны с ошибками. В вашем случае вам нужно громко жаловаться, если есть нечетное количество записей.Я часто делаю это, предотвращая запуск программы, где это возможно, если только данные не являются экстернализированными, в этот момент вы просто хотите громко жаловаться и, возможно, вернуться к предыдущим значениям, чтобы не сломать производственный сервер с плохими данными.

Если вы можете определить имя и «значение», вы должны это сделать.

Я делаю это совсем немного - просто помните, что всякий раз, когда вы удаляете проверку времени компиляции с таким кодом, вы принимаете на себя ответственность за выполнение во время выполнения!

Кстати, вы также можете улучшить как проверку ошибок, так и читаемость (запятая используется только как предложение, вы просто выбираете какой-то разделитель, который не может идти ни в одном поле, запятой, двоеточием и тильди - это хороший выбор):

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]) 
} 

Я использую этот шаблон с инициализацией массива строк постоянно. Вы можете включать такие вещи, как имена методов, чтобы привязывать ваши данные кодовым отражением.

Боковые преимущества этого в том, что на самом деле очень легко сделать экстернализацию, обеспечив возможность конфигурации и экстернализации во время выполнения без дополнительной работы.

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% типизированного, потому что пары ключ-значение представленный на уровне статического типа.

Кстати, это не зависит в основном от особенностей 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; 
} 
+0

Это удивительность в лучшем случае :) –

+0

Thanks, Rohit; от вас это много значит :) –

+1

Lol .. Мое удовольствие :) И теперь я буду использовать это в своих проектах. –

2

Если вы думаете об этом, читаемость и безопасность тесно взаимосвязаны. Чрезвычайно читаемый код настолько прозрачен, что любая ошибка формируется на фоне простоты выражения вашей идеи. Чрезвычайно безопасный код должен быть доступен для чтения, потому что, делая сложные проверки кода, вы получаете ошибки, допущенные при последующем обслуживании.

Здесь нет конфликта. Все, что вы сделаете, чтобы сделать реализацию вспомогательного метода более безопасным, сделает его использование более читаемым.

Например, ему не нужно брать строки, он может принимать пары строк (вспомогательный класс). Или он может проверить разумность ввода. Или вы можете использовать метод put, как показано в вашем вопросе. Любая из этих альтернатив делает результат более безопасным и более читаемым.

"Readable" означает, что вам не нужно тщательно изучать шаблон каждый раз, когда вы его видите, не так ли?