L'accesso predefinito non rende ridondante il modificatore di accesso privato.
La posizione dei progettisti linguistici su questo si riflette nel tutorial ufficiale - Controllo dell'accesso ai membri di una classe ed è abbastanza chiaro (per tua comodità, la dichiarazione pertinente nella citazione diventa grassetto ):
Suggerimenti sulla scelta di un livello di accesso:
Se altri programmatori usano la tua classe, vuoi assicurarti che non si possano verificare errori da uso improprio. I livelli di accesso possono aiutarti a farlo.
- Utilizzare il livello di accesso più restrittivo che ha senso per un determinato membro. Usa privato a meno che tu non abbia una buona ragione per non farlo.
- Evita i campi pubblici ad eccezione delle costanti. (Molti degli esempi del tutorial utilizzano campi pubblici. Questo può aiutare a illustrare alcuni punti in modo conciso, ma non è raccomandato per il codice di produzione.) I campi pubblici tendono a collegarti a una particolare implementazione e limitare la tua flessibilità nella modifica del codice.
Il tuo appello alla testabilità come giustificazione per eliminare completamente il modificatore privato è sbagliato, come evidenziato ad esempio dalle risposte in Nuovo al TDD. Devo evitare i metodi privati adesso?
Ovviamente puoi avere metodi privati e ovviamente puoi provarli.
O c'è un modo per far funzionare il metodo privato, nel qual caso puoi provarlo in quel modo, o non c'è modo di far funzionare il privato, nel qual caso: perché diamine stai provando a provarlo, solo elimina la dannata cosa ...
La posizione dei progettisti linguistici sullo scopo e sull'uso dell'accesso a livello di pacchetto è spiegata in un altro tutorial ufficiale, Creazione e utilizzo di pacchetti e non ha nulla in comune con l'idea di abbandonare modificatori privati (per comodità, la dichiarazione pertinente nella citazione diventa grassetto ) :
È necessario raggruppare queste classi e l'interfaccia in un pacchetto per diversi motivi, tra cui:
- Tu e altri programmatori potete facilmente determinare che questi tipi sono correlati ...
- Puoi consentire ai tipi all'interno del pacchetto di avere accesso illimitato l'uno all'altro, ma ancora limitare l'accesso per i tipi al di fuori del pacchetto ...
<rant "Penso di aver sentito abbastanza piagnistei. Suppongo sia giunto il momento di dire forte e chiaro ...">
I metodi privati sono utili per i test unitari.
La nota che segue presuppone che tu abbia familiarità con la copertura del codice . In caso contrario, prenditi del tempo per imparare, dal momento che è abbastanza utile per coloro che sono interessati ai test unitari e ai test.
Va bene, quindi ho quel metodo privato e unit test e l'analisi di copertura mi dice che c'è un divario, il mio metodo privato non è coperto da test. Adesso...
Cosa ottengo dal mantenerlo privato
Poiché il metodo è privato, l'unico modo per procedere è studiare il codice per scoprire come viene utilizzato tramite API non private. Tipicamente, un tale studio rivela che la ragione del divario è che nei test manca uno scenario di utilizzo particolare.
void nonPrivateMethod(boolean condition) {
if (condition) {
privateMethod();
}
// other code...
}
// unit tests don't invoke nonPrivateMethod(true)
// => privateMethod isn't covered.
Per completezza, altri motivi (meno frequenti) per tali lacune di copertura potrebbero essere errori nella specifica / progettazione. Non mi immergerò in profondità qui, per semplificare le cose; basti dire che se si indebolisce la limitazione dell'accesso "solo per rendere testabile il metodo", si perderà la possibilità di apprendere che questi bug esistono affatto.
Bene, per correggere il divario, aggiungo un test unitario per lo scenario mancante, ripeto l'analisi della copertura e verifico che il divario è scomparso. Cosa ho adesso? Ho come nuovo test unitario per l'utilizzo specifico di API non private.
Il nuovo test garantisce che il comportamento previsto per questo utilizzo non cambierà senza preavviso poiché se cambia, il test fallirà.
Un lettore esterno può esaminare questo test e imparare come dovrebbe usare e comportarsi (qui, il lettore esterno include il mio sé futuro, poiché tendo a dimenticare il codice un mese o due dopo aver finito con esso).
Il nuovo test è tollerante al refactoring (refactoring metodi privati? Scommetti!) Qualunque cosa io faccia privateMethod
, voglio sempre testare nonPrivateMethod(true)
. Indipendentemente da ciò che faccio privateMethod
, non sarà necessario modificare il test perché il metodo non viene richiamato direttamente.
Non male? Scommetti.
Cosa perdo dall'indebolimento della limitazione dell'accesso
Ora immagina che invece di sopra, ho semplicemente indebolito la limitazione dell'accesso. Salto lo studio del codice che utilizza il metodo e procedo direttamente con il test che invoca il mio exPrivateMethod
. Grande? Non!
Ottengo un test per l'utilizzo specifico dell'API non privata di cui sopra? No: non è stato effettuato alcun test per nonPrivateMethod(true)
prima e non esiste un test del genere ora.
I lettori esterni hanno la possibilità di comprendere meglio l'uso della classe? No. "- Hey, qual è lo scopo del metodo testato qui? - Dimenticalo, è strettamente per uso interno. - Oops."
È tollerante al refactoring? Assolutamente no: qualunque cosa io cambi exPrivateMethod
, probabilmente interromperà il test. Rinomina, unisci in qualche altro metodo, modifica gli argomenti e il test smetterà di compilare. Mal di testa? Scommetti!
Riassumendo , attenersi al metodo privato mi porta a un utile, affidabile miglioramento nei test unitari. Al contrario, l'indebolimento delle limitazioni di accesso "per testabilità" mi dà solo un codice di test oscuro, difficile da capire, che è inoltre a rischio permanente di essere infranto da qualsiasi refactoring minore; francamente ciò che ottengo sembra sospettosamente un debito tecnico .
</ Rant>