Ho appena avuto una discussione su una scelta di design dopo una revisione del codice. Mi chiedo quali siano le tue opinioni.
C'è questa Preferences
classe, che è un secchio per le coppie chiave-valore. I valori nulli sono legali (è importante). Prevediamo che alcuni valori potrebbero non essere ancora stati salvati e vogliamo gestirli automaticamente inizializzandoli con un valore predefinito predefinito quando richiesto.
La soluzione discussa utilizzava il seguente modello (NOTA: questo non è il codice reale, ovviamente - è semplificato a scopi illustrativi):
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();
}
}
}
È stato criticato come anti-modello - abusando delle eccezioni per controllare il flusso . KeyNotFoundException
- portato alla vita solo per quel caso d'uso - non sarà mai visto fuori dallo scopo di questa classe.
Sono essenzialmente due metodi che giocano a prendere con esso semplicemente per comunicare qualcosa tra loro.
La chiave non presente nel database non è qualcosa di allarmante o eccezionale: ci aspettiamo che ciò avvenga ogni volta che viene aggiunta una nuova impostazione delle preferenze, quindi il meccanismo che la inizializza con garbo con un valore predefinito, se necessario.
La controargomentazione era che getValueByKey
- il metodo privato - come definito in questo momento non ha modo naturale di informare il metodo pubblico sia sul valore, sia sulla chiave presente. (In caso contrario, deve essere aggiunto in modo che il valore possa essere aggiornato).
Il ritorno null
sarebbe ambiguo, dal momento che null
è un valore perfettamente legale, quindi non si può dire se significasse che la chiave non c'era, o se c'era un null
.
getValueByKey
dovrebbe restituire una sorta di a Tuple<Boolean, String>
, il bool impostato su true se la chiave è già lì, in modo che possiamo distinguere tra (true, null)
e (false, null)
. (Un out
parametro potrebbe essere usato in C #, ma è Java).
È un'alternativa più bella? Sì, dovresti definire una classe monouso per l'effetto di Tuple<Boolean, String>
, ma poi ci stiamo liberando KeyNotFoundException
, quindi quel tipo di equilibri. Stiamo anche evitando il sovraccarico di gestire un'eccezione, anche se non è significativo in termini pratici - non ci sono considerazioni sulle prestazioni di cui parlare, è un'app client e non è come le preferenze dell'utente verranno recuperate milioni di volte al secondo.
Una variante di questo approccio potrebbe usare Guava Optional<String>
(Guava è già usato in tutto il progetto) invece di qualche abitudine Tuple<Boolean, String>
, e quindi possiamo distinguere tra Optional.<String>absent()
"corretto" null
. Sembra comunque hacker per ragioni che sono facili da vedere - l'introduzione di due livelli di "nullità" sembra abusare del concetto che stava dietro la creazione di Optional
s in primo luogo.
Un'altra opzione sarebbe quella di verificare esplicitamente se la chiave esiste (aggiungere un boolean containsKey(String key)
metodo e chiamare getValueByKey
solo se abbiamo già affermato che esiste).
Infine, si potrebbe anche incorporare il metodo privato, ma l'attuale getByKey
è in qualche modo più complesso del mio esempio di codice, quindi l'allineamento lo farebbe sembrare piuttosto brutto.
Potrei spaccare i capelli qui, ma sono curioso di sapere su cosa scommetteresti per essere il più vicino alle migliori pratiche in questo caso. Non ho trovato una risposta nelle guide di stile di Oracle o Google.
L'uso di eccezioni come nel codice di esempio è un anti-pattern o è accettabile dato che le alternative non sono molto pulite? Se lo è, in quali circostanze andrebbe bene? E viceversa?
getValueByKey
è anche pubblica.