Я просто обговорював вибір дизайну після огляду коду. Цікаво, що ваша думка.
Є цей 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- реалізований лише для одного випадку використання - ніколи не буде розглянуто за межами цього класу.
Це, по суті, два способи грати з ним, щоб просто повідомити щось одне одному.
Ключ, який не присутній у базі даних, не є чимось тривожним чи винятковим - ми очікуємо, що це відбудеться кожного разу, коли буде додано нове налаштування параметрів, отже, механізм, який витончено ініціалізує його за значенням за замовчуванням, якщо це необхідно.
Контраргументом було те, getValueByKeyщо приватний метод - як визначено зараз, не має природного способу інформування публічного методу як про значення, так і про те, чи був ключ. (Якщо цього не було, його потрібно додати, щоб значення можна було оновити).
Повернення nullбуло б неоднозначним, оскільки nullце цілком юридична цінність, тому немає жодної інформації про те, чи означає це, що ключа не було, чи є null.
getValueByKeyдоведеться повернути якесь a Tuple<Boolean, String>, bool встановлено на true, якщо ключ вже є, щоб ми могли розрізняти (true, null)і (false, null). ( outПараметр може бути використаний у C #, але це Java).
Це приємніша альтернатива? Так, вам доведеться визначити деякий клас одноразового використання Tuple<Boolean, String>, але тоді ми позбудемося KeyNotFoundException, і такий вид врівноважується. Ми також уникаємо накладних витрат на обробку винятків, хоча це практично не є важливим з точки зору практичної роботи - це не стосується продуктивності, це клієнтське додаток, і це не так, як переваги користувачів будуть виведені мільйони разів за секунду.
Варіант цього підходу може використовувати «Гуаву» Optional<String>(Guava вже використовується у всьому проекті), а не якийсь звичайний Tuple<Boolean, String>, і тоді ми можемо розрізняти Optional.<String>absent()«належне» null. Він все ще відчуває хакерство, проте з легких причин - введення двох рівнів "нікчемності", здається, зловживає концепцією, яка стояла за створенням Optionals в першу чергу.
Іншим варіантом було б чітко перевірити, чи існує ключ (додати boolean containsKey(String key)метод і викликати, getValueByKeyлише якщо ми вже стверджували, що він існує).
Нарешті, можна також вкласти приватний метод, але фактичний getByKeyє дещо складнішим, ніж мій зразок коду, таким чином, вкладка зробить це виглядати досить неприємно.
Я можу тут розщеплювати волоски, але мені цікаво, на що ви б зробили ставку, щоб у цьому випадку було найближчим до найкращої практики. Я не знайшов відповіді в посібниках зі стилів Oracle чи Google.
Чи використання винятків, таких як у зразку коду, є антидіаграмою, чи прийнятно, враховуючи, що альтернативи теж не дуже чисті? Якщо це так, за яких обставин це було б добре? І навпаки?
getValueByKeyвоно також є публічним.