È eccessivo avvolgere una raccolta in una classe semplice solo per motivi di migliore leggibilità?


15

Ho la seguente mappa:

Map<Double, List<SoundEvent>> soundEventCells = new HashMap<Double, List<SoundEvent>>();

Questo HashMapmappa i doublevalori (che sono punti nel tempo) alla corrispondente SoundEvent"cella": ogni "cella" può contenere un numero di SoundEvents. Ecco perché è implementato come List<SoundEvent>, perché è esattamente quello che è.

Per motivi di migliore leggibilità del codice, ho pensato di implementare una classe interna statica molto semplice come questa:

private static class SoundEventCell {
    private List<SoundEvent> soundEvents = new ArrayList<SoundEvent>();
    public void addEvent(SoundEvent event){
        soundEvents.add(event);
    }
    public int getSize(){
        return soundEvents.size();
    }
    public SoundEvent getEvent(int index){
        return soundEvents.get(index);
    }
    // .. remove() method unneeded
}

E rispetto alla dichiarazione della mappa (e molti altri codici) apparirebbe meglio, ad esempio:

Map<Double, SoundEventCell> soundEventCells = new HashMap<Double, SoundEventCell>();

Questo è eccessivo? Lo faresti nei tuoi progetti?


si potrebbe sostenere che concettualmente, questo è stato affrontato in Come sapresti se hai scritto un codice leggibile e facilmente gestibile? Se i tuoi coetanei continuano a lamentarsi del tuo modo di fare le cose, sia in un modo che nell'altro, è meglio cambiare per farli stare meglio
moscerino

1
Cos'è che rende l'elenco di eventi sonori una "cella" anziché un elenco? Questa scelta di parole significa che una cella ha o alla fine avrà un comportamento diverso rispetto a un elenco?
x-code,

@DocBrown Perché? La classe è private staticperché verrà utilizzata solo dalla classe esterna, ma non è correlata a nessuna istanza specifica della classe esterna. Non è esattamente l'uso corretto di private static?
Aviv Cohn,

2
@Doc Brown, Aviv Cohn: non esiste alcun tag che specifichi una lingua, quindi tutto può essere giusto e sbagliato allo stesso tempo!
Emilio Garavaglia,

@EmilioGaravaglia: Java (penso che sia abbastanza chiaro dal momento che a giudicare dalla sintassi potrebbe essere Java o C #, e le convenzioni utilizzate lo restringono a Java;)).
Aviv Cohn,

Risposte:


12

Non è affatto eccessivo. Inizia con le operazioni che ti servono, piuttosto che iniziare con "Posso usare una HashMap". A volte una HashMap è proprio ciò di cui hai bisogno.
Nel tuo caso sospetto che non lo sia. Quello che probabilmente vuoi fare è qualcosa del genere:

public class EventsByTime {
    public EventsByTime addEvent(double time, SoundEvent e);
    public List<SoundEvent> getEvents(double time);
    // ... more methods specific to your use ...
}

Sicuramente non vuoi avere un sacco di codice che dice questo:

List<SoundEvent> events = eventMap.get(time);
if (events == null) {
   events = new ArrayList<SoundEvent>();
   eventMap.put(time, events);
}

O forse potresti semplicemente usare una delle implementazioni di Guava Multimap .


Quindi sostenete di usare una classe, che è essenzialmente un meccanismo per nascondere le informazioni, come un ... meccanismo per nascondere le informazioni? L'orrore.
Robert Harvey,

1
In realtà sì, ho una TimeLineclasse esattamente per quel tipo di cose :) È un involucro sottile attorno a un HashMap<Double, SoundEventCell>(alla fine sono andato con l' idea SoundEventCellinvece List<SoundEvent>dell'idea). Quindi posso solo fare timeline.addEvent(4.5, new SoundEvent(..))e incapsulare le cose di livello inferiore :)
Aviv Cohn,

14

Mentre può aiutare nella leggibilità in alcune aree, può anche complicare le cose. Personalmente mi allontano dal confezionare o estendere le raccolte per motivi di fluidità, poiché il nuovo wrapper, alla lettura iniziale, implica per me che potrebbe esserci un comportamento di cui devo essere consapevole. Consideralo una sfumatura del Principio della minima sorpresa.

Attenersi all'implementazione dell'interfaccia significa che devo solo preoccuparmi dell'interfaccia. L'implementazione concreta può ovviamente comportare un comportamento aggiuntivo, ma non dovrei preoccuparmene. Quindi, quando sto cercando di trovare la mia strada attraverso il codice di qualcuno, preferisco le semplici interfacce per la leggibilità.

Se, d'altra parte, si sta trovando un caso d'uso che fa beneficio dal comportamento aggiunto, allora hai un argomento per migliorare il codice creando una classe pieno titolo.


11
Un wrapper può anche essere usato per rimuovere (o nascondere) comportamenti non necessari.
Roman Reiner,

4
@RomanReiner - Vorrei mettere in guardia contro tali cose. Il comportamento non necessario oggi è spesso il programmatore che maledice il tuo nome domani. Tutti sanno cosa si Listpuò fare e fa tutte queste cose per una buona ragione.
Telastyn,

Apprezzo il desiderio di mantenere la funzionalità, anche se penso che la soluzione sia un attento equilibrio tra funzionalità e astrazione. SoundEventCellpotrebbe implementare Iterableper SoundEvents, che offrirebbe l'iteratore del soundEventsmembro, in modo da poter leggere (ma non scrivere) come qualsiasi elenco. Esito a mascherare la complessità quasi quanto esito a usare un Listquando potrei aver bisogno di qualcosa di più dinamico in futuro.
Neil,

2

L'avvolgimento limita la tua funzionalità solo ai metodi che decidi di decidere di scrivere, aumentando sostanzialmente il tuo codice senza alcun vantaggio. Per lo meno, proverei quanto segue:

private static class SoundEventCell : List<SoundEvent>
{
}

Puoi ancora scrivere il codice dal tuo esempio.

Map<Double, SoundEventCell> soundEventCells = new HashMap<Double, SoundEventCell>();

Detto questo, l'ho mai fatto solo quando ci sono alcune funzionalità di cui l'elenco stesso ha bisogno. Ma penso che il tuo metodo sarebbe eccessivo per questo. A meno che tu non abbia avuto un motivo per voler limitare l'accesso alla maggior parte dei metodi di List.


-1

Un'altra soluzione potrebbe essere quella di definire la classe wrapper con un singolo metodo che espone l'elenco:

private static class SoundEventCell
{
    private List<SoundEvent> events;

    public SoundEventCell(List<SoundEvent> events)
    {
        this.events = events;
    }

    public List<SoundEvent> getEvents()
    {
        return events;
    }
}

Questo ti dà la tua classe ben nominata con un codice minimo, ma ti dà comunque l'incapsulamento, permettendoti ad esempio di rendere immutabile la classe (facendo una copia difensiva nel costruttore e usando Collections.unmodifiableListnell'accessorio).

(Tuttavia, se questi elenchi vengono effettivamente utilizzati solo in questa classe, penso che faresti meglio a sostituire il tuo Map<Double, List<SoundEvent>>con un Multimap<Double, SoundEvent>( docs ), poiché ciò spesso salva molta logica ed errori di controllo null.)

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.