Qual è il vantaggio dell'incapsulamento di una raccolta all'interno di una classe?

3

Vedi il codice qui sotto:

      public class Customer
    {
        private readonly IList<Order> _orders = new List<Order>();

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Province { get; set; }
        public IEnumerable<Order> Orders { get { return _orders; } } //This line

        internal void AddOrder(Order order)
        {
            _orders.Add(order);
        }
}

Questo è quello che mi aspetterei di vedere. Sto guardando un codice, tuttavia la riga che ho identificato con un commento è stata sostituita con questa:

 public OrderCollection Orders { get; } = new OrderCollection();

Qual è il vantaggio dell'incapsulamento dell'IEnumerable in una classe come questa? Sto parlando dal punto di vista del DDD - un'area che sto cercando di migliorare.

    
posta w0051977 27.11.2017 - 20:18
fonte

4 risposte

9

Il problema con il tuo esempio di codice originale è che Orders interrompe l'incapsulamento di _orders mentre stai restituendo l'elenco. Trasmetti IEnumerable<Order> a IList<Order> e il codice all'esterno della classe può iniziare a modificare i contenuti.

La mia ipotesi è quindi che OrderCollection venga usato per evitare che ciò si verifichi. Tuttavia, non è necessario farlo: yield return offre un modo molto più semplice di mantenere l'incapsulamento:

public class Customer
{
    private readonly IList<Order> _orders = new List<Order>();

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Province { get; set; }
    public IEnumerable<Order> Orders 
    {
        get { foreach (var order in _orders) yield return order; }
    }

    internal void AddOrder(Order order)
    {
        _orders.Add(order);
    }
}  
    
risposta data 27.11.2017 - 22:36
fonte
1

Dopo aver riletto la tua domanda, potresti chiederti qual è il vantaggio dell'uso di OrderCollection su List<Order> . Questo non è quello che inizialmente ho risposto (vedi sotto per la risposta originale).

Non ho visto le raccolte specifiche di classe comunemente usate da quando Java e C # hanno introdotto i generici nelle loro lingue. In origine era l'unico modo per garantire che la raccolta funzionasse solo con oggetti Order . Se questo è il caso, c'è no benefit.

Ho visto casi speciali in cui esistono motivi specifici per avere una lezione di raccolta dedicata:

  • C'è una logica complicata per gestire lo spostamento dell'ordine di elementi in un elenco: l'esempio potrebbe essere un elenco di elementi da elaborare in cui la priorità può variare tra le iterazioni.
  • Esistono metodi di riepilogo associati alla raccolta.
  • O qualche altro problema specifico dell'applicazione che devi risolvere.

Le funzionalità linguistiche attuali possono rendere un punto controverso la maggior parte delle ragioni per una classe dedicata per la raccolta. Ad esempio, le funzioni di estensione in C # possono funzionare su qualsiasi IEnumrable<Order> non solo su quello speciale.

L'unico posto nella storia recente che potrei giustificare un incapsulamento speciale di una raccolta aveva a che fare con le regole di prioritizzazione dinamica. Avevamo una coda di elaborazione round-robin per i dati in arrivo, ma alcuni eventi avrebbero contrassegnato alcuni selettori come inattivi o il ciclo si sarebbe dovuto interrompere se avesse impiegato più di X secondi. Le regole erano molto specifiche per la gestione delle code di lavoro. Ogni N secondi il processore si svegliava e iterava sull'elenco dei selettori attivi per lavorare. Non l'abbiamo chiamato collezione, ma è essenzialmente quello che era. Abbiamo utilizzato yield return e yield break per gestire la restituzione del prossimo live selector o interrompere il ciclo di elaborazione. L'iterazione successiva ha dovuto riprendere nel punto in cui era stata interrotta.

Al di là di questo esempio, il sovraccarico di manutenzione di avere un'implementazione di raccolta dedicata raramente è meglio che utilizzare semplicemente una raccolta generica standard.

L'unica differenza tra i due esempi di codice che hai mostrato era dovuta a una caratteristica di C # introdotta in .Net 4.6 chiamato proprietà di sola lettura . Estende il concetto di proprietà auto introdotto in .Net 4.

Questa:

private OrderCollection _orders = new OrderCollection();

public OrderCollection Orders { get { return _orders; } }

È funzionalmente identico a questo:

public OrderCollection Orders { get; } = new OrderCollection();

L'unica differenza è che il compilatore genera la variabile di supporto e la inizializza con il valore sul lato destro. Quando accedi alla proprietà Orders , c'è solo un getter in entrambe le istanze.

Ora, l'isolamento completo del dettaglio dell'implementazione OrderCollection è indirizzato dalla risposta di David Amo. La sua soluzione è fondamentalmente diversa in quanto si espone solo un IEnumerable<Order> e non c'è modo di ottenere un riferimento a una collezione mutabile come il codice originale.

L'enumerazione immutabile è fondamentalmente diversa (per gentile concessione di David Amo):

private OrderCollection _orders = new OrderCollection();

public IEnumerable<Order> Orders
{
    get { foreach (var order in _orders) yield return order; }
}

.Net 4.6 ha anche introdotto proprietà lambda che assomiglia a questo:

public string FullName => string.Join(" ", FirstName, LastName);
    
risposta data 28.11.2017 - 20:42
fonte
1

Forse l'intenzione non era solo quella di nascondere l'implementazione di una singola raccolta. Forse hai più raccolte di tipi diversi (array, elenco, dizionario) a cui è necessario accedere? Quindi dovresti incapsulare e accedere attraverso una singola interfaccia. Controlla il pattern iteratore.

    
risposta data 23.12.2017 - 08:02
fonte
0

OrderCollection sembra il tipo di classe che dovevamo creare all'inizio del XXI secolo prima dei generici.

Qual è il vantaggio di usare questa classe? È inconoscibile senza vedere la classe.

Una classe dovrebbe proteggere i suoi dati da modifiche indesiderate. L'esposizione di un elenco consente ad altre classi di aggiungere, rimuovere, ecc.

Un'opzione in questo scenario è di esporre la raccolta come ReadOnlyCollection<T> .

public class DontMessWithMyList<T>
{
    private readonly List<T> _myPrivateList;

    public ReadOnlyCollection<T> ListContents => _myPrivateList.AsReadOnly();
}

Se la tua lista interna è esposta in questo modo e il suo contenuto è immutabile, le altre classi non possono modificare nulla. Possono solo vederlo.

    
risposta data 22.12.2017 - 20:25
fonte

Leggi altre domande sui tag