Perché definire un oggetto Java usando l'interfaccia (ad es. Mappa) anziché l'implementazione (HashMap)


17

Nella maggior parte del codice Java, vedo le persone dichiarare oggetti Java in questo modo:

Map<String, String> hashMap = new HashMap<>();
List<String> list = new ArrayList<>();

invece di:

HashMap<String, String> hashMap = new HashMap<>();
ArrayList<String> list = new ArrayList<>();

Perché esiste una preferenza per definire l'oggetto Java usando l'interfaccia piuttosto che l'implementazione che verrà effettivamente utilizzata?


Risposte:


26

Il motivo è che l'implementazione di queste interfacce di solito non è rilevante quando le si maneggia, quindi se si obbliga il chiamante a passare HashMapun metodo, quindi si sta essenzialmente obbligando quale implementazione utilizzare. Quindi, come regola generale, dovresti gestire la sua interfaccia anziché l'implementazione effettiva ed evitare il dolore e la sofferenza che potrebbero comportare la modifica di tutte le firme del metodo utilizzandoHashMap quando decidi che devi usare LinkedHashMapinvece.

Va detto che ci sono eccezioni a ciò quando l'implementazione è rilevante. Se hai bisogno di una mappa quando l'ordine è importante, puoi richiedere il passaggio di a TreeMapo LinkedHashMapa, o meglio, SortedMapche non specifica un'implementazione specifica. Ciò obbliga il chiamante a passare necessariamente un certo tipo di implementazione di Map e suggerisce fortemente che l'ordine è importante. Detto questo, potresti scavalcarlo SortedMape passarne uno non ordinato? Sì, certo, tuttavia si aspettano che accadano cose brutte.

Tuttavia, le migliori pratiche impongono ancora che, se non è importante, non è necessario utilizzare implementazioni specifiche. Questo è vero in generale. Se hai a che fare con Doge da Catcui derivano Animal, al fine di sfruttare al meglio l'eredità, dovresti generalmente evitare di avere metodi specifici per Dogo Cat. Piuttosto tutti i metodi dentro Dogo Catdovrebbero sovrascrivere i metodi Animale ti farà risparmiare problemi nel lungo periodo.


Quando è necessaria una mappa ordinata, il tipo di parametro dovrebbe essere SortedMap, non TreeMap.
Cefalopode

@Arian SortedMapè una delle numerose implementazioni che si occupano di ordinare. Questo è oltre il punto. TreeMapordina inoltre gli articoli in base all'implementazione della chiave Comparableo all'interfaccia fornita Comparator.
Neil,

No, SortedMap non è un'implementazione, questo è esattamente il punto. È l'interfaccia per le mappe che ordinano per chiave.
Cefalopode

1
@Arian Ah capisco cosa intendi. Mappa Ordinata, migliore poiché non impone un'implementazione. Farò le opportune modifiche.
Neil

In realtà, a LinkedHashMapnon implementa SortedMap. Le uniche sottoclassi di SortedMapsono ConcurrentSkipListMape TreeMap.
bcorso,

10

Nelle parole di Layman:

Lo stesso motivo per cui i produttori di impianti elettrici hanno costruito i loro prodotti con spine elettriche anziché semplicemente staccare i cavi e le case sono dotate di prese a muro anziché di cavi staccati che sporgono dal muro.

Utilizzando invece spine standard, consentono di collegare le stesse caratteristiche in qualsiasi spina compatibile in casa.

Dal punto di vista della presa a muro, non importa se si collega un televisore o uno stereo.

Ciò rende più utili sia l'apparecchio che la presa.

Prendiamo ad esempio un metodo che accetta una mappa come argomento.

Il metodo funzionerà indipendentemente dal fatto che gli passi una HashMap o una LinkedHashMap, purché sia ​​una sottoclasse di Map.

Questo è il principio di sostituzione di Liskov .

Nel codice di esempio che hai fornito, significa che puoi in seguito, per qualche motivo, cambiare l'implementazione concreta di Hash e non dovrai cambiare il resto del codice.

Il problema con il software è che, poiché è relativamente facile cambiare le cose in un secondo momento senza sprecare mattoni o malta, le persone presumono che quel tipo di pensiero preliminare non valga la pena. Ma la realtà ci ha mostrato che la manutenzione del software è molto costosa.


4

È seguire il principio di segregazione dell'interfaccia (l'io in SOLID ). Impedisce al codice che utilizza quegli oggetti di dipendere dai metodi di quegli oggetti di cui non ha bisogno, il che rende il codice meno accoppiato e quindi più facile da modificare.

Ad esempio, se lo scopri in seguito hai davvero bisogno di un LinkedHashMap , puoi tranquillamente apportare quella modifica senza influire su nessun altro codice.

Tuttavia, c'è un compromesso, perché stai limitando artificialmente il codice che può prendere il tuo oggetto come parametro. Supponiamo che ci sia una funzione da qualche parte che richiede un HashMapper qualche motivo. Se si restituisce a Map, non è possibile passare l'oggetto in quella funzione. Devi bilanciare la probabilità che in futuro occorra la funzionalità extra che è nella classe più concreta con il desiderio di limitare l'accoppiamento e mantenere la tua interfaccia pubblica il più piccola possibile.


3

Avere la variabile vincolata a un'interfaccia garantisce che nessuno degli usi di quella variabile verrà utilizzato HashMap funzionalità specifiche che potrebbero non esistere sull'interfaccia, quindi l'istanza può essere cambiata senza preoccupazioni in seguito a un'implementazione diversa fintanto che la nuova istanza implementa anche il interfaccia.

Per questo motivo, ogni volta che si desidera utilizzare un'interfaccia di oggetti, è sempre buona norma dichiarare le variabili come interfaccia e non la particolare implementazione, questo vale per tutti i tipi di oggetti che è possibile utilizzare che hanno un'interfaccia. Il motivo per cui lo vedi spesso è che molte persone lo hanno inserito come un'abitudine.

Detto questo, non è dannoso evitare di usare le interfacce a volte, e la maggior parte di noi sciatta non segue sempre questa regola, senza alcun danno reale. È solo una buona pratica attenersi a quando si ha la sensazione che il codice possa essere modificato e che necessiti di manutenzione / crescita in futuro. È meno preoccupante quando stai hackerando un codice che non sospetti avrà una lunga vita o abbia molta importanza. Anche infrangere questa regola di solito ha una piccola conseguenza che cambiare l'implementazione in un'altra potrebbe richiedere un po 'di refactoring, quindi se non la segui sempre non ti ferirai molto, anche se non c'è alcun danno reale nel seguirla .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.