Я просто обговорював вибір дизайну після огляду коду. Цікаво, що ваша думка.
Є цей 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
. Він все ще відчуває хакерство, проте з легких причин - введення двох рівнів "нікчемності", здається, зловживає концепцією, яка стояла за створенням Optional
s в першу чергу.
Іншим варіантом було б чітко перевірити, чи існує ключ (додати boolean containsKey(String key)
метод і викликати, getValueByKey
лише якщо ми вже стверджували, що він існує).
Нарешті, можна також вкласти приватний метод, але фактичний getByKey
є дещо складнішим, ніж мій зразок коду, таким чином, вкладка зробить це виглядати досить неприємно.
Я можу тут розщеплювати волоски, але мені цікаво, на що ви б зробили ставку, щоб у цьому випадку було найближчим до найкращої практики. Я не знайшов відповіді в посібниках зі стилів Oracle чи Google.
Чи використання винятків, таких як у зразку коду, є антидіаграмою, чи прийнятно, враховуючи, що альтернативи теж не дуже чисті? Якщо це так, за яких обставин це було б добре? І навпаки?
getValueByKey
воно також є публічним.