Design del modello di oggetto: raccolte su classi

6

Considera Train.Passengers, che tipo useresti per i Passeggeri in cui i passeggeri non dovrebbero essere aggiunti o rimossi dal codice che consuma?

Sto usando .NET Framework, quindi questa discussione si adatterebbe a .NET, ma potrebbe applicarsi a una serie di linguaggi / framework moderni.

In .NET Framework, il List<Person> non dovrebbe essere esposto pubblicamente. C'è Collection<Person> e ICollection<Person> e guidance, che tendo a concordare, è quello di restituire il tipo concreto più vicino nell'albero di ereditarietà, in modo che sarebbe Collection<Person> poiché è già un ICollection<Person> .

Ma la collezione ha semantica di lettura / scrittura e quindi forse dovrebbe essere un ReadOnlyCollection<Person> , ma è probabilmente il suo buonsenso non modificare il contenuto di una raccolta di cui non si ha conoscenza intima, quindi è necessario?

E richiede un lavoro extra internamente e può essere un problema con la (de) serializzazione.

All'estremo opposto potrei semplicemente restituire Person[] (dato che LINQ ora fornisce molti dei benefici che in precedenza sarebbero stati offerti da una raccolta più specifica) o persino creare un PersonCollection o ReadOnlyPersonCollection strongmente tipizzati!

Che cosa fai?

    
posta Luke Puplett 07.01.2011 - 14:57
fonte

5 risposte

4

Il modo più semplice è usare IEnumerable<Passenger> . Se hai esposto un List<Passenger> come IEnumerable, potresti preoccuparti che qualcuno lo trasmetta a un Elenco, ma personalmente non lo farei. Stanno rompendo il contratto se lo fanno, e tu non sei qui per impedirgli di spararsi ai piedi se vogliono. Il tuo compito è comunicare chiaramente ciò che dovrebbero fare con esso.

Se vuoi fornire la possibilità di ottenere un conteggio dei passeggeri o di recuperare un passeggero per indice, hai due opzioni:

  1. Esporre metodi / proprietà pubbliche: public int PassengerCount { get; } , public Passenger GetPassengerByIndex(int index) (e potresti sbarazzarti dell'IEnumerable se lo desideri)
  2. Esporre un ReadOnlyCollection<Passenger> anziché IEnumerable<Passenger> che accetta una lista nel suo costruttore.

Personalmente ritengo che la prima opzione sia migliore se stai definendo un'API pubblica perché ti offre più spazio per modificare l'implementazione senza influire sull'interfaccia. Inoltre, se il consumatore deve chiamare train.Passengers.Count , questo tipo di interruzione di uno dei principi OO (sui metodi di chiamata sul risultato dei metodi di un oggetto).

C'è un'altra differenza tra le due opzioni se la tua classe Train non è immutabile. Se ottieni un riferimento a train.Passengers e tienilo premuto, e poi qualcuno scende dal treno, quel cambiamento non si riflette nel riferimento che hai. Se usi la prima opzione, intendi che il numero di passeggeri che ricevi è solo "a partire da questo momento". Quindi la prima opzione implica la mutabilità, ma la seconda opzione implica l'immutabilità (almeno nella lista dei passeggeri).

    
risposta data 07.01.2011 - 17:15
fonte
2

Di solito quando passi le raccolte il modo più flessibile è passare IEnumerable < class >

Se hai davvero bisogno di impedire ad altri di modificare la tua raccolta che non funzionerà, dato che possono essere lanciati nell'Elenco originale o qualunque cosa fosse, allora ReadOnlyCollection sembra una buona scelta.

    
risposta data 07.01.2011 - 15:39
fonte
2

Non ho mai visto alcuna documentazione Microsoft che specifichi che un elenco non debba essere esposto pubblicamente. Se si osserva la definizione Elenco classi su MSDN, non si troverà alcuna menzione di questo . Dovrai fornire la documentazione per eseguire il backup delle asserzioni.

Nelle nostre applicazioni usiamo di solito Elenco (Of T) per le nostre collezioni. Sono semplici, digitano sicuri e quando li esponiamo tramite un servizio Web (e così via) vengono convertiti automaticamente in una matrice semplice (Of T). Abbiamo utilizzato questo approccio su innumerevoli sistemi senza molti problemi.

Modifica

Ho letto i post nei commenti, grazie a tutti per aver fornito la documentazione. Per noi, estendere le nostre lezioni non è un problema e il fatto che la serializzazione sia un problema con le raccolte ci mette nel campo List / IList.

Anche se sono d'accordo sul fatto che le raccomandazioni in generale abbiano senso, sono solo raccomandazioni e che se la tua situazione non si adatta a quel modello, allora dovresti fare ciò che funziona per te. Nel nostro caso esponiamo List (Of T) o talvolta implementiamo il nostro IList.

    
risposta data 07.01.2011 - 16:16
fonte
0

In Java, si restituisce un Set e si utilizza Collections.unmodifiableSet per rendere il set da restituire. Immagino che questo business ReadOnlyCollection / asReadOnly sia il tuo equivalente.

IEnumerable non mi sembra una buona scelta. Voglio poter dire:

Passenger roger = rememberRoger();
if (train.getPassengers().contains(roger)) {
    tellEveryoneRogerIsOnHisWayHome();
}

Ciò significa che getPassengers deve restituire l'equivalente di Raccolta.

    
risposta data 08.01.2011 - 00:04
fonte
-3

Considererei i comportamenti dei passeggeri. Per esempio. se non vuoi comportamenti forniti da tipi di base, è meglio creare tipi personalizzati ...

In Object Oriented un oggetto dovrebbe esistere solo se ha "comportamenti" (vedi ad esempio cosa dice di questo Allen Holub). Quindi, la mia risposta significa che nella mia domanda progetterò Passeggeri come raccolta di Passeggero se non avrò alcun comportamento aggiuntivo. Ma una volta trovato ho bisogno di un comportamento come "rimuovere tutti i Passeggeri sotto i 10 anni", creerò Passeggeri come oggetto personalizzato con una raccolta interna che mostra un messaggio "removePassengersYoungerThan (int years)".

Penso che ogni collezione abbia bisogno di un design specifico. Anche nella stessa applicazione.

Spero che questo sia più chiaro della risposta precedente.

    
risposta data 02.04.2014 - 23:35
fonte

Leggi altre domande sui tag