// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
//                         marc.chevaldonne.free.fr
//
// Module        : MonLit.cs
// Author        : Marc Chevaldonné
// Creation date : 2016-09-30
//
// ========================================================================

using System;
using System.Collections.Generic;

namespace ex_023_017_Deep_Read_only_collections_dll
{
    /// <summary>
    /// L'exemple suivant montre comment encapsuler une collection d'un type dans le cas suivant :
    /// => le type est mutable, c'est-à-dire qu'on peut modifier son état interne en appelant ses méthodes ou propriétés
    ///     => MonLit possède une collection privée mNounours de Nounours où Nounours est mutable
    /// Comment encapsuler correctement une collection d'un tel type ?
    /// Dans l'exemple suivant, nous voulons protéger la collection contre :
    /// - les modifications de la collection (ajout ou suppression d'éléments),
    /// - la modification de la collection elle-même (on ne doit pas pouvoir faire de new)
    /// - la modification de chaque élément de la collection : on ne doit pas pouvoir faire de new, ni appeler des méthodes ou propriétés mutables.
    /// 
    /// En résumé, nous cherchons à rendre publique en lecture seule, la collection privée de nounours du lit, ainsi que chaque élément.
    /// 
    /// L'exemple 023_015 montrait comment encapsuler une collection d'un type immuable ou d'un type volontairement laissé mutable.
    /// Tous les mauvais cas présentés dans l'exemple 023_015 sont toujours mauvais ici, et ne sont donc pas représentés.
    /// Le solution qui avait été retenu dans l'exemple 023_015 consistait à utiliser un wrapper ReadOnlyCollection.
    /// Cette solution n'est pas envisageable si le type est mutable et que nous cherchons à le protéger.
    /// 
    /// La bonne solution consiste à :
    /// - rendre le type interne (Nounours)
    ///     c'est la raison pour laquelle cet exemple est sur deux assemblages :
    ///         . une bibliothèque de classes contenant le type interne et une classe contenant une collection privée de ce type ;
    ///         . un exécutable consommant la classe contenant la collection à encapsuler.
    /// - faire une façade immuable publique de ce type (INounours)
    ///     cette façade immuable ne contient que des propriétés et méthodes en lecture seule
    /// - profiter de la covariance des interfaces (IEnumerable, IReadOnlyCollection et IReadOnlyList)
    ///   et encapsuler la collection privée du type mutable interne avec une collection en lecture seule de la façade immuable
    /// => en effet : 
    ///     + les types IEnumerable, IReadOnlyCollection et IReadOnlyList sont des collections en lecture seule, elles ne sont donc pas modifiables
    ///     + elles ne peuvent pas être castées en une collection modifiable du type mutable à l'extérieur de la bibliothèque de classes car celui-ci est interne à la bibliothèque
    ///     + elles ne peuvent pas être castées en une collection modificable de la façade immuable car les classes (comme List, Array, Queue...) ne sont pas covariantes.
    /// => C'EST GAGNE !!!
    public class MonLit
    {
        //on veut encapsuler une collection d'un type muable
        private List<Nounours> mNounours = new List<Nounours>(new Nounours[] {
                                    new Nounours(1, "Beluga", new DateTime(2012, 07, 29), 0),
                                    new Nounours(2, "Singe", new DateTime(2009, 08, 09), 1345),
                                    new Nounours(3, "Girafe", new DateTime(2007, 11, 02), 567)});

        //Les solutions suivantes sont toutes OK si Nounours reste internal à la bibliothèque et INounours public en façade immuable

        /// <summary>
        /// Avec un IEnumerable, aucun membre n'est exposé, on ne peut que parcourir la collection (type de plus haut niveau)
        /// </summary>
        public IEnumerable<INounours> MesNounoursIEnumerable
        {
            get
            {
                return mNounours;
            }
        }

        /// <summary>
        /// IReadOnlyCollection expose la propriété Count en plus de ce qu'autorise IEnumerable
        /// </summary>
        public IReadOnlyCollection<INounours> MesNounoursIReadOnlyCollection
        {
            get
            {
                return mNounours;
            }
        }

        /// <summary>
        /// IReadOnlyList expose un indexeur en plus de ce qu'autorise IReadOnlyCollection
        /// </summary>
        public IReadOnlyList<INounours> MesNounoursIReadOnlyList
        {
            get
            {
                return mNounours;
            }
        }
    }
}
