Recentemente ho letto molto su TDD e codice pulito, quindi ho iniziato a lavorare su un progetto semplice che li mette in pratica e mi sono imbattuto in qualcosa che non sono sicuro di quale sia l'approccio migliore da adottare.I chiamanti devono controllare la validità degli argomenti prima di chiamare il costruttore?
Ho una classe che accetta un oggetto Java File
come parametro, l'aspettativa è che questo oggetto File
deve essere una directory e deve iniziare con un certo prefisso. Il mio primo passaggio consisteva nel fare controlli sull'oggetto File
prima di chiamare il costruttore, controllando che fosse una directory e controllando che il nome fosse valido. Ma non mi piace che sia il chiamante che sta specificando ciò che lo rende valido e in particolare quello che è il prefisso valido, penso che questa logica dovrebbe essere collocata nella classe stessa.
Potrei fare questo controllo nel costruttore e lanciare un'eccezione se non è valida, ma data la natura del problema, se sto iterando su un elenco di File
s allora è del tutto previsto che alcuni di loro hanno vinto 'essere' valido '(vale a dire che saranno file piuttosto che directory), quindi è un lancio di un Exception
davvero giustificato?
public MyObject(File directory) {
if (!directory.isDirectory()) {
throw new IllegalArgumentException("Must be a directory");
}
if (!directory.getName().startsWith("Prefix")) {
throw new IllegalArgumentException("Must start with Prefix");
}
....
}
ho pensato di aggiungere forse un metodo factory per creare gli oggetti e restituendo null se l'File
non è valido.
public static MyObject createMyObject(File directory) {
if (!directory.isDirectory() || !directory.getName().startsWith("Prefix")) {
return null;
}
return new MyObject(directory);
}
In alternativa ho pensato di aggiungere un metodo statico alla classe che convalida il file per il chiamante prima di chiamare il costruttore.
public static boolean isValid(File directory) {
return directory.isDirectory() && directory.getName().startsWith("Prefix");
}
if (MyObject.isValid(directory)) {
MyObject object = new MyObject(directory);
}
Quindi, in termini di codice pulito e di tutti i principi di programmazione orientata agli oggetti (come la singola responsabilità, accoppiamento, ecc), che sarebbe il modo migliore di fare questo?
UPDATE:
Avendo letto alcune delle risposte che sono state inserite già ho cominciato a pensare a un'altra possibilità che le sarebbero applicabili solo la mia situazione attuale, piuttosto che genericamente come la mia domanda era veramente.
Come parte del mio codice chiamante ho un percorso dal filesystem e sto elencando tutti i file in quella directory ed è ogni file che sto passando al costruttore MyObject se è valido o meno. Potrei passare un FileFilter
al metodo listFiles
che garantisce che listFiles restituisca solo directory valide. Il FileFilter
poteva essere dichiarato entro MyObject:
public static FileFilter getFilter() {
return new FileFilter() {
public boolean accept(File path) {
return path.isDirectory() && path.getName().startsWith("Prefix");
}
};
}
Se il mio costruttore ha generato un'eccezione allora sarebbe davvero essere una situazione eccezionale, perché l'aspettativa è che è solo stato passato directory valide. Fare questo significherebbe che potrei rimuovere la necessità di un'eccezione controllata da costruttore/factory poiché qualsiasi eccezione indicherà un bug da qualche parte piuttosto che un comportamento previsto. Ma lascia ancora la questione se metterlo in un costruttore o in un metodo di fabbrica.
Per me, il terzo sarebbe preferibile. Più breve e puoi usarlo con un numero di file. Inoltre, non è necessario restituire un nuovo oggetto dal metodo isValid e creare l'oggetto solo se isValid restituisce true da un'altra parte del codice. Questo è buono in quanto separa le funzionalità. –
@AliAlamiri In termini di codice pulito è la mia preferenza perché la lettura ha più senso, cioè è chiaro al lettore cosa sta succedendo.Non riesco a decidere se mi piace il fatto che il chiamante debba essere consapevole che deve chiamare prima Valid. – DaveJohnston
Perché non passare il file al costruttore e controllare se il file è valido. Questo nasconde il controllo dal chiamante mentre costruisci oggetti e chiedi al costruttore di controllare i file che vengono passati? –