Hermetyzacja, Lista i IEnumerable

0

Czytam sobie "Head First". Wydanie 3. Tam, w rozdziale 8 jest zdanie:

It’s great for encapsulation, too. If you expose an IEnumerable<T> instead of, say, a List<T>, then you can’t accidentally
write code that modifies it. 

co na polski zostało przetłumaczone:

Rozwiązanie to poprawia także hermetyczność klasy. Jeśli udostępnisz składową typu IEnumerable<T>, a nie na przykład List<T>, to nikt nie będzie mógł przypadkowo napisać kodu, który zmodyfikuje zawartość kolekcji.

Czy mógłby ktoś podać mi przykład, o co tu chodzi?
Przecież mogę rzutować, jak poniżej, i dobierać się do zawartości tej kolekcji.

using System;
using System.Collections.Generic;

namespace Hermetyzacja_Lista_IEnumerable {
    class Program {
        private static Konto konto;
        static void Main(string[] args) {
            Console.WriteLine("Gostek założył konto ...");
            konto = new Konto();
            Console.WriteLine("i wpłacił kasę ...");
            for (int i = 0; i < 5; i++) {
                konto.ZałóżLokatę(10000, "PLN");
            }
            Console.WriteLine("i z zadowoleniem sprawdził stan:\n\r");
            konto.Raport();
            Console.WriteLine("ale ktoś mu zrobił kawał ...\n\r");
            List<Lokata> lokaty = (List<Lokata>)konto.Lokaty;
            lokaty.Clear();
            for (int i = 0; i < 5; i++)
            {
                konto.ZałóżLokatę(10000, "BYR");
            }
            Console.WriteLine("i następnego dnia zobaczył:\n\r");
            konto.Raport();
            Console.ReadKey();
        }
    }
    public class Lokata {
        private int kwota;
        private string waluta;
        public Lokata(int ile, string wCzym) {
            kwota = ile;
            waluta = wCzym;
        }
        public override string ToString() {
            return kwota.ToString("## ##0 ") + waluta;
        }
    }
    public class Konto
    {
        private List<Lokata> lokaty;
        public Konto() {
            lokaty = new List<Lokata>();
        }
        public void ZałóżLokatę(int ile, string wCzym) {
            lokaty.Add(new Lokata(ile, wCzym));
        }
        public void Raport() {
            Console.WriteLine("Stan konta:");
            for (int i = 0; i < lokaty.Count; i++) {
                Console.WriteLine("Lokata nr " + (i + 1) + ": " + lokaty[i]);
            }
            Console.WriteLine("");
        }
        public IEnumerable<Lokata> Lokaty {
            get { return lokaty; }
        }
    }
}
1

Dobrze jest napisane - nie zmodyfikujesz przypadkowo, jeśli pole klasy jest IEnumerable to wiesz ze intencją autora było udostępnienie czegoś niemodyfikowalnego.

A to że jak bardzo będziesz chciał to i tak zmienisz to inna sprawa.

0
Wybitny Mleczarz napisał(a):

Dobrze jest napisane - nie zmodyfikujesz przypadkowo, jeśli pole klasy jest IEnumerable to wiesz ze intencją autora było udostępnienie czegoś niemodyfikowalnego.

A to że jak bardzo będziesz chciał to i tak zmienisz to inna sprawa.

To by oznaczało, że zamiast:

public IEnumerable<Lokata> Lokaty {
            get { return lokaty; }
        }

mogę równie dobrze napisać:

public List<Lokata> Lokaty {
            get { return lokaty; }
        }

I tego też "przypadkowo" nie modyfikować. Tylko dzięki czemu została poprawiona hermetyczność. Przecież przy obu rozwiązaniach można modyfikować. To gdzie ta poprawa?

0

No taka poprawa że dla typu List <T> możesz sobie od razu np. dodać element do kolekcji, bez żadnego rzutowania.

0

Nikt więcej się nie wypowie? Jeśli dobrze rozumiem przedpiścę, to mam uwierzyć, że dobra zmiana jest dobra.

1

Słowem kluczowym jest "przypadkowo". Rzutowanie IEnumerable na typ listy naprawdę ciężko jest zrobić przypadkowo. Nie chodzi tutaj o zabezpieczenie przed "hakjerami" tylko przed samym sobą i swoimi pomyłkami bomożemy przekazywać instancję listy przez wiele klas i gdzieś głęboko ją przypadkowo zmodyfikować bo zapomnieliśmy zrobić kopię.
Tylko że tu lepiej sprawdzi się zwracanie IReadOnlyCollection, nie powinniśmy z listy robić enumerable bo klasa korzystająca nie może skorzystać z faktu że lista już jest w pamięci i nie jest wyliczana.

A modyfikować można zawsze choćby przez refleksję czy grzebanie po pamięci - w ogóle nie o to chodzi

0

Dziękuję Ci, wielorako zwący się przedpiśco, że nie zostawiłeś mnie bez odpowiedzi. Udało Ci się zmusić mnie do większego zaangażowania się i odpytywania wujka. Podpowiedział mi, żeby zastosować listę interfejsów. I, przynajmniej na dzisiejszy poziom mojej wiedzy, to mi wystarczy.

0

Poza tym, nie tylko List<T> jest IEnumerable<T>.
A w programowaniu chodzi o to, żeby używać kontraktów dostarczanych przez klasy, a nie wnikać w szczegóły implementacji każdej z nich.

1 użytkowników online, w tym zalogowanych: 0, gości: 1