Dlaczego pola prywatne są prywatne dla typu, a nie dla instancji?

153

W C # (i wielu innych językach) dostęp do prywatnych pól innych wystąpień tego samego typu jest całkowicie uzasadniony. Na przykład:

public class Foo
{
    private bool aBool;

    public void DoBar(Foo anotherFoo)
    {
        if (anotherFoo.aBool) ...
    }
}

Ponieważ specyfikacja C # (sekcje 3.5.1, 3.5.2) stwierdza, że ​​dostęp do pól prywatnych dotyczy typu, a nie wystąpienia. Rozmawiałem o tym z kolegą i próbujemy wymyślić powód, dla którego to działa (zamiast ograniczać dostęp do tej samej instancji).

Najlepszym argumentem, jaki mogliśmy wymyślić, są sprawdzenia równości, w których klasa może chcieć uzyskać dostęp do pól prywatnych, aby określić równość z inną instancją. Czy są jakieś inne powody? Czy może jakiś złoty powód, który absolutnie oznacza, że ​​musi tak działać, czy coś byłoby całkowicie niemożliwe?

RichK
źródło
18
Jakie są korzyści z rozwiązania alternatywnego?
Jon Skeet
19
Prawdą jest, że nie modeluje zbyt dobrze świata rzeczywistego. W końcu to, że mój samochód może raportować, ile paliwa zostało, nie oznacza, że ​​mój samochód powinien być w stanie określić, ile paliwa zostało KAŻDY samochód. Więc tak, teoretycznie widzę, że mam dostęp do „instancji prywatnej”. Czuję, że naprawdę nigdy bym go nie użył, ponieważ martwiłbym się, że w 99% przypadków naprawdę CHCIAŁBYM mieć dostęp do tej zmiennej innej instancji.
aquinas
2
@Jon Stanowisko przyjęte dla tzw. „Korzyści” jest takie, że klasy nie powinny znać stanu wewnętrznego innej instancji - niezależnie od tego, że jest ona tego samego typu. Żadna korzyść na jedno słowo, ale prawdopodobnie jest całkiem dobry powód, aby wybrać jedną z nich i właśnie to próbowaliśmy zrozumieć
RichK
4
Zawsze można zrobić zmienną w parę zamknięć getter / setter, zainicjowanego w konstruktorze, który zakończy się niepowodzeniem, jeśli to nie jest takie samo jak to było w czasie inicjalizacji ...
Martin Sojka
8
Scala zapewnia prywatnych członków instancji - wraz z wieloma innymi poziomami dostępu, których nie ma w Javie, C # i wielu innych językach. To jest całkowicie uzasadnione pytanie.
Bruno Reis,

Odpowiedzi:

73

Myślę, że jednym z powodów, dla których działa to w ten sposób, jest to, że modyfikatory dostępu działają w czasie kompilacji . W związku z tym określenie, czy dany obiekt jest również bieżącym obiektem, nie jest łatwe do wykonania. Na przykład rozważ ten kod:

public class Foo
{
    private int bar;

    public void Baz(Foo other)
    {
        other.bar = 2;
    }

    public void Boo()
    {
        Baz(this);
    }
}

Czy kompilator może koniecznie dowiedzieć się, że othertak jest this? Nie we wszystkich przypadkach. Można by argumentować, że to po prostu nie powinno się wtedy kompilować, ale oznacza to, że mamy ścieżkę kodu, w której prywatny element członkowski właściwej instancji nie jest dostępny, co moim zdaniem jest jeszcze gorsze.

Wymagając tylko zamiast poziomu typu obiekt poziomu widoczności, zapewnia, że problem jest łagodny, a także co do sytuacji, że wydaje się, że powinien działać właściwie pracę.

EDYCJA : Twierdzenie Danilela Hilgartha, że ​​to rozumowanie jest wsteczne, ma wartość. Projektanci języka mogą stworzyć żądany język, a autorzy kompilatorów muszą się do niego dostosować. Biorąc to pod uwagę, projektanci języków mają pewną motywację, aby ułatwić pisarzom kompilatorów wykonywanie ich pracy. (Chociaż w tym przypadku łatwo jest argumentować, że prywatni członkowie mogliby wtedy uzyskać dostęp tylko przez this(niejawnie lub jawnie)).

Uważam jednak, że to sprawia, że ​​problem jest bardziej zagmatwany niż powinien. Większość użytkowników (łącznie ze mną) uznałoby to za niepotrzebne ograniczenie, gdyby powyższy kod nie działał: w końcu to moje dane, do których próbuję uzyskać dostęp! Dlaczego mam przez to przechodzić this?

Krótko mówiąc, myślę, że mogłem przesadzić, ponieważ jest to „trudne” dla kompilatora. Tak naprawdę chciałem się zorientować, że powyższa sytuacja wygląda na taką, którą projektanci chcieliby mieć.

dlev
źródło
33
IMHO, to rozumowanie jest niewłaściwe. Kompilator wymusza reguły języka. To ich nie robi. Innymi słowy, gdyby członkowie prywatni byli „prywatni instancji”, a nie „prywatni typu”, kompilator zostałby zaimplementowany w inny sposób, np. Tylko zezwalając, this.bar = 2ale nie other.bar = 2, ponieważ othermoże to być inna instancja.
Daniel Hilgarth
@Daniel To dobra uwaga, ale jak wspomniałem, myślę, że posiadanie tego ograniczenia byłoby gorsze niż posiadanie widoczności na poziomie klasy.
dlev 08
3
@dlev: Nie powiedziałem nic o tym, czy uważam, że członkowie „instancji prywatnej” są dobrą rzeczą. :-) Chciałem tylko zwrócić uwagę, że argumentujesz, iż implementacja techniczna dyktuje reguły języka i moim zdaniem ta argumentacja nie jest poprawna.
Daniel Hilgarth
2
@Daniel Point zajęty. Nadal uważam, że jest to ważna uwaga, chociaż masz oczywiście rację, że ostatecznie projektanci języka wiedzą, czego chcą, a twórcy kompilatorów dostosowują się do tego.
dlev
2
Nie sądzę, aby argument kompilatora był prawidłowy. Istnieją języki, które zezwalają tylko na prywatne instancje (jak Ruby) i które pozwalają na prywatne instancje i klasy prywatne z wyboru (jak Eiffel). Generowanie kompilatora nie było konieczne ani trudniejsze, ani łatwiejsze dla tych języków. Aby uzyskać więcej informacji, zobacz także moją odpowiedź w wątku.
Abel
61

Ponieważ celem rodzaju hermetyzacji używanej w C # i podobnych językach * jest zmniejszenie wzajemnej zależności różnych fragmentów kodu (klas w C # i Javie), a nie różnych obiektów w pamięci.

Na przykład, jeśli piszesz kod w jednej klasie, która używa niektórych pól w innej klasie, to te klasy są bardzo ściśle powiązane. Jeśli jednak masz do czynienia z kodem, w którym masz dwa obiekty tej samej klasy, nie ma dodatkowej zależności. Klasa zawsze zależy od siebie.

Jednak cała ta teoria dotycząca hermetyzacji zawodzi, gdy tylko ktoś utworzy właściwości (lub pary get / set w Javie) i bezpośrednio ujawni wszystkie pola, co sprawia, że ​​klasy są tak sprzężone, jakby i tak uzyskiwały dostęp do pól.

* Wyjaśnienie dotyczące rodzajów kapsułkowania znajduje się w doskonałej odpowiedzi Abla.

Goran Jovic
źródło
3
Nie sądzę, że zawodzi, gdy get/setzostaną udostępnione metody. Metody dostępu nadal zachowują abstrakcję (użytkownik nie musi wiedzieć, że za nim znajduje się pole ani jakie pola mogą znajdować się za właściwością). Na przykład, jeśli piszemy Complexklasę, możemy ujawnić właściwości, aby uzyskać / ustawić współrzędne biegunowe, a inne pobrać / ustawić dla współrzędnych kartezjańskich. Który z nich jest używany przez klasę pod spodem, jest nieznany użytkownikowi (może to być nawet coś zupełnie innego).
Ken Wayne VanderLinde
5
@Ken: Nie powiedzie się, jeśli podasz właściwość dla każdego posiadanego pola, nawet nie zastanawiając się, czy powinno zostać ujawnione.
Goran Jovic
@Goran Chociaż zgadzam się, że nie byłoby to idealne rozwiązanie, nadal nie kończy się to niepowodzeniem, ponieważ metody dostępu get / set pozwalają na dodanie dodatkowej logiki, jeśli na przykład zmienią się podstawowe pola.
ForbesLindesay,
1
„Ponieważ celem hermetyzacji jest zmniejszenie wzajemnej zależności różnych fragmentów kodu” >> nie. Hermetyzacja może również oznaczać hermetyzację instancji, zależy to od języka lub szkoły myślenia. Jeden z najbardziej zdecydowanych głosów w OO, Bertrand Meyer, uważa to nawet za domyślne dla private. Jeśli chcesz, możesz sprawdzić moją odpowiedź na mój punkt widzenia;).
Abel
@Abel: Oparłem swoją odpowiedź na językach z enkapsulacją klas, ponieważ OP zapytał o jeden z nich i szczerze mówiąc, ponieważ je znam. Dzięki za korektę i nowe ciekawe informacje!
Goran Jovic
49

Sporo odpowiedzi zostało już dodanych do tego interesującego wątku, jednak nie do końca znalazłem prawdziwy powód, dla którego takie zachowanie jest takie, jakie jest. Spróbuję:

W dawnych czasach

Gdzieś pomiędzy Smalltalk w latach 80-tych a Javą w połowie lat 90-tych dojrzewała koncepcja orientacji obiektowej. Ukrywanie informacji, które pierwotnie nie było uważane za koncepcję dostępną tylko dla OO (wspomniane po raz pierwszy w 1978 r.), Zostało wprowadzone w Smalltalk, ponieważ wszystkie dane (pola) klasy są prywatne, wszystkie metody są publiczne. Podczas wielu nowych opracowań OO w latach 90-tych Bertrand Meyer próbował sformalizować wiele koncepcji OO w swojej przełomowej książce Object Oriented Software Construction (OOSC), która od tego czasu jest uważana za (prawie) ostateczne odniesienie do koncepcji OO i projektowania języka .

W przypadku prywatnej widoczności

Według Meyera metoda powinna być dostępna dla określonego zbioru klas (str. 192-193). Daje to oczywiście bardzo dużą szczegółowość ukrywania informacji, następująca funkcja jest dostępna dla klasy A i klasy B oraz wszystkich ich potomków:

feature {classA, classB}
   methodName

W przypadku privateon mówi co następuje: bez jawnego zadeklarowania typu jako widocznego dla własnej klasy, nie możesz uzyskać dostępu do tej cechy (metody / pola) w wywołaniu kwalifikowanym. To xznaczy, jeśli jest zmienną, x.doSomething()nie jest dozwolone. Niekwalifikowany dostęp jest oczywiście dozwolony wewnątrz samej klasy.

Innymi słowy: aby zezwolić na dostęp instancji tej samej klasy, musisz jawnie zezwolić tej klasie na dostęp do metody. Jest to czasami nazywane prywatnymi instancjami i klasami prywatnymi.

Instancja prywatna w językach programowania

Znam co najmniej dwa obecnie używane języki, w których ukrywa się prywatne informacje instancji w przeciwieństwie do ukrywania prywatnych informacji klasowych. Jednym z nich jest Eiffel, język zaprojektowany przez Meyera, który przenosi OO do skrajności. Drugim jest Ruby, obecnie o wiele bardziej powszechny język. W Rubim privateoznacza: „prywatny dla tej instancji” .

Wybory dotyczące projektowania języka

Sugerowano, że zezwolenie na prywatność instancji byłoby trudne dla kompilatora. Nie sądzę, ponieważ stosunkowo łatwo jest po prostu zezwolić lub zabronić kwalifikowanych wywołań metod. Jeśli w przypadku metody prywatnej doSomething()jest to dozwolone, a x.doSomething()nie, projektant języka skutecznie zdefiniował dostępność tylko dla instancji dla prywatnych metod i pól.

Z technicznego punktu widzenia nie ma powodu, aby wybierać jedną lub drugą stronę (zwłaszcza biorąc pod uwagę, że Eiffel.NET może to zrobić z IL, nawet w przypadku dziedziczenia wielokrotnego, nie ma żadnego powodu, aby nie zapewniać tej funkcji).

Oczywiście jest to kwestia gustu i jak już inni wspomnieli, całkiem niektóre metody mogą być trudniejsze do napisania bez funkcji widoczności na poziomie klasy prywatnych metod i pól.

Dlaczego C # umożliwia tylko hermetyzację klas, a nie hermetyzację wystąpień

Jeśli spojrzysz na wątki internetowe dotyczące hermetyzacji instancji (termin używany czasami w odniesieniu do faktu, że język definiuje modyfikatory dostępu na poziomie instancji, a nie na poziomie klasy), koncepcja ta jest często źle widziana. Jednak biorąc pod uwagę, że niektóre współczesne języki używają enkapsulacji instancji, przynajmniej dla modyfikatora dostępu prywatnego, sprawia, że ​​myślisz, że może być i jest użyteczne we współczesnym świecie programowania.

Jednak C # najwyraźniej najtrudniej przyjrzał się C ++ i Javie ze względu na projekt języka. Podczas gdy Eiffel i Modula-3 również były na obrazku, biorąc pod uwagę wiele brakujących funkcji Eiffla (dziedziczenie wielokrotne), uważam, że wybrali tę samą trasę, co Java i C ++, jeśli chodzi o modyfikator dostępu prywatnego.

Jeśli naprawdę chcesz wiedzieć, dlaczego powinieneś spróbować skontaktować się z Ericiem Lippertem, Krzysztofem Cwaliną, Andersem Hejlsbergiem lub kimkolwiek innym, kto pracował nad standardem C #. Niestety, nie mogłem znaleźć ostatecznej notatki w opatrzonym adnotacjami języku programowania C # .

Abel
źródło
1
To urocza odpowiedź z dużym tłem, bardzo interesująca, dzięki za
poświęcenie
1
@RichK: Proszę bardzo, po prostu złapał pytanie zbyt późno, jak sądzę, ale okazało się, że temat intrygujące wystarczy, aby odpowiedzieć pewnej głębokości :)
Abel
W modelu COM „prywatne” oznaczało „prywatne wystąpienie”. Myślę, że było to w pewnym sensie, ponieważ definicja klasy Fooskutecznie reprezentowała interfejs i klasę implementującą; ponieważ COM nie miał koncepcji odwołania do obiektu klasy - tylko odwołania do interfejsu - nie było możliwości, aby klasa mogła przechowywać odniesienie do czegoś, co było gwarantowane jako kolejna instancja tej klasy. Ten projekt oparty na interfejsie utrudniał niektóre rzeczy, ale oznaczał, że można zaprojektować klasę, którą można zastąpić inną, bez konieczności współużytkowania którejkolwiek z tych samych elementów wewnętrznych.
supercat
2
Świetna odpowiedź. OP powinien rozważyć oznaczenie tego pytania jako odpowiedzi.
Gavin Osborn
18

To tylko moja opinia, ale pragmatycznie myślę, że jeśli programista ma dostęp do źródła klasy, można rozsądnie zaufać mu w dostępie do prywatnych członków instancji klasy. Po co wiązać prawą rękę programisty, skoro w lewej dałeś im już klucze do królestwa?

FishBasketGordo
źródło
13
Nie rozumiem tego argumentu. Powiedzmy, że pracujesz nad programem, w którym jesteś jedynym programistą i jedynym, który NIGDY używałby twoich bibliotek, czy mówisz, że w takim przypadku upubliczniasz KAŻDEGO członka, ponieważ masz dostęp do kodu źródłowego?
aquinas
@aquinas: to jest zupełnie inne. Upublicznianie wszystkiego doprowadzi do okropnych praktyk programistycznych. Umożliwienie typowi zobaczenia prywatnych pól innych jego instancji jest bardzo wąskim przypadkiem.
Igby Largeman
2
Nie, nie zalecam upubliczniania wszystkich członków. Niebiosa, nie! Mój argument jest taki, że jeśli już jesteś w źródle, widzisz prywatnych członków, jak są używane, stosowane konwencje itp., To dlaczego nie móc skorzystać z tej wiedzy?
FishBasketGordo
7
Myślę, że sposób myślenia wciąż jest typowy. Jeśli typowi nie można ufać, że prawidłowo poradzi sobie z członkami typu, co może? ( Zwykle byłby to programista faktycznie kontrolujący interakcje, ale w dobie narzędzi do generowania kodu może to nie zawsze mieć miejsce.)
Anthony Pegram
3
Moje pytanie nie dotyczy zaufania, a raczej konieczności. Zaufanie jest całkowicie nieistotne, kiedy możesz użyć refleksji do ohydnych rzeczy dla szeregowych. Zastanawiam się, dlaczego koncepcyjnie możesz uzyskać dostęp do szeregowych. Założyłem, że był raczej logiczny powód niż sytuacja wynikająca z najlepszych praktyk.
RichK
13

Przyczyną jest rzeczywiście sprawdzanie równości, porównywanie, klonowanie, przeciążanie operatorów ... Na przykład wprowadzenie operatora + na liczbach zespolonych byłoby bardzo trudne.

Lorenzo Dematté
źródło
3
Ale wszystko, o czym wspominasz, wymaga, aby jeden typ POBIERAŁ wartość innego typu. Jestem na pokładzie mówiąc: chcesz sprawdzić mój stan prywatny? Dobra, śmiało. Chcesz ustawić mój stan prywatny? Nie ma mowy, kolego, zmień swój cholerny stan. :)
aquinas
2
@aquinas To nie działa w ten sposób. Pola to pola. Są tylko do odczytu, jeśli zostały zadeklarowane jako, readonlya następnie dotyczy to również deklarującego wystąpienia. Najwyraźniej zapewniasz, że powinna istnieć określona reguła kompilatora, która zabrania tego, ale każda taka reguła jest funkcją, którą zespół C # musi określić, udokumentować, zlokalizować, przetestować, utrzymywać i wspierać. Jeśli nie ma nieodpartej korzyści, dlaczego mieliby to robić?
Aaronaught
Zgadzam się z @Aaronaught: implementacja każdej nowej specyfikacji i zmiany w kompilatorze wiąże się z kosztami. W przypadku ustalonego scenariusza rozważ metodę Clone. Jasne, możesz chcieć to zrealizować z prywatnym konstruktorem, ale może w niektórych scenariuszach nie jest to możliwe / wskazane.
Lorenzo Dematté
1
Zespół C #? Właśnie debatuję nad możliwymi teoretycznymi zmianami języka w DOWOLNYM języku. „Jeśli nie ma nieodpartej korzyści…” Myślę, że może być korzyść. Podobnie jak w przypadku każdej gwarancji kompilatora, ktoś może uznać za przydatne zagwarantowanie, że klasa modyfikuje tylko swój własny stan.
aquinas
@aquinas: Funkcje są wdrażane, ponieważ przydatne dla wielu lub większości użytkowników, a nie dlatego, że mogą być przydatne w niektórych pojedynczych przypadkach.
Aaronaught,
9

Po pierwsze, co by się stało z prywatnymi statycznymi członkami? Czy można uzyskać do nich dostęp tylko za pomocą metod statycznych? Na pewno byś tego nie chciał, ponieważ wtedy nie byłbyś w stanie uzyskać dostępu do swojegoconst plików.

Jeśli chodzi o twoje wyraźne pytanie, rozważ przypadek a StringBuilder, który jest zaimplementowany jako połączona lista własnych instancji:

public class StringBuilder
{
    private string chunk;
    private StringBuilder nextChunk;
}

Jeśli nie możesz uzyskać dostępu do prywatnych członków innych instancji Twojej własnej klasy, musisz zaimplementować w ToStringten sposób:

public override string ToString()
{
    return chunk + nextChunk.ToString();
}

To zadziała, ale to O (n ^ 2) - niezbyt wydajne. W rzeczywistości to prawdopodobnie podważa cały cel posiadania StringBuilderklasy na pierwszym miejscu. Jeśli możesz uzyskać dostęp do prywatnych członków innych instancji Twojej własnej klasy, możesz to zaimplementować ToString, tworząc ciąg o odpowiedniej długości, a następnie wykonując niebezpieczną kopię każdego fragmentu w odpowiednim miejscu w ciągu:

public override string ToString()
{
    string ret = string.FastAllocateString(Length);
    StringBuilder next = this;

    unsafe
    {
        fixed (char *dest = ret)
            while (next != null)
            {
                fixed (char *src = next.chunk)
                    string.wstrcpy(dest, src, next.chunk.Length);
                next = next.nextChunk;
            }
    }
    return ret;
}

Ta implementacja to O (n), dzięki czemu jest bardzo szybka i jest możliwa tylko wtedy, gdy masz dostęp do prywatnych członków innych instancji Twojej klasy .

Gabe
źródło
Tak, ale czy nie ma innych sposobów na obejście tego, na przykład ujawnienie niektórych pól jako wewnętrznych?
MikeKulls
2
@Mike: Odsłonięcie pola internalsprawia, że ​​jest ono mniej prywatne! Skończyło się na tym, że wystawiałbyś wnętrze swojej klasy na wszystko, co się składa.
Gabe
3

Jest to całkowicie uzasadnione w wielu językach (C ++ na przykład). Modyfikatory dostępu pochodzą z zasady hermetyzacji w OOP. Chodzi o to, aby ograniczyć dostęp do zewnątrz , w tym przypadku na zewnątrz są inne klasy. Na przykład każda klasa zagnieżdżona w C # może również uzyskać dostęp do swoich prywatnych członków nadrzędnych.

Chociaż jest to wybór projektu dla projektanta języka. Ograniczenie tego dostępu może bardzo skomplikować niektóre bardzo typowe scenariusze, nie przyczyniając się zbytnio do izolacji jednostek.

Jest podobna dyskusja tutaj

Mehran
źródło
1

Nie sądzę, żeby istniał powód, dla którego nie moglibyśmy dodać kolejnego poziomu prywatności, w którym dane są prywatne w każdej instancji. W rzeczywistości może to nawet zapewnić przyjemne poczucie kompletności językowi.

Ale w praktyce wątpię, żeby to było naprawdę przydatne. Jak zauważyłeś, nasza zwykła prywatność jest przydatna do rzeczy takich jak sprawdzanie równości, a także do większości innych operacji obejmujących wiele instancji typu. Chociaż podoba mi się również twój punkt widzenia dotyczący utrzymania abstrakcji danych, ponieważ jest to ważny punkt w OOP.

Myślę dookoła, zapewnienie możliwości ograniczenia dostępu w taki sposób może być fajną funkcją do dodania do OOP. Czy to naprawdę przydatne? Powiedziałbym, że nie, ponieważ klasa powinna móc ufać własnemu kodowi. Ponieważ ta klasa jest jedyną rzeczą, która ma dostęp do prywatnych elementów członkowskich, nie ma prawdziwego powodu, aby potrzebować abstrakcji danych w przypadku wystąpienia innej klasy.

Oczywiście zawsze możesz napisać swój kod tak, jakby był prywatny zastosowany do instancji. Użyj zwykłych get/setmetod, aby uzyskać dostęp do danych / zmienić je. To prawdopodobnie uczyniłoby kod łatwiejszym w zarządzaniu, gdyby klasa mogła podlegać wewnętrznym zmianom.

Ken Wayne Vander Linde
źródło
0

Świetne odpowiedzi podane powyżej. Dodałbym, że częścią tego problemu jest fakt, że tworzenie instancji klasy wewnątrz siebie jest nawet dozwolone. To sprawia, że ​​na przykład w pętli rekurencyjnej "for" używa się tego typu sztuczek, o ile masz logikę do zakończenia rekurencji. Ale tworzenie instancji lub przekazywanie tej samej klasy w sobie bez tworzenia takich pętli logicznie stwarza własne niebezpieczeństwa, mimo że jest to powszechnie akceptowany paradygmat programowania. Na przykład klasa C # może utworzyć wystąpienie swojej kopii w domyślnym konstruktorze, ale nie łamie to żadnych reguł ani nie tworzy pętli przyczynowych. Czemu?

A tak przy okazji… ten sam problem dotyczy również członków „chronionych”. :(

Nigdy w pełni nie zaakceptowałem tego paradygmatu programowania, ponieważ nadal wiąże się on z całym zestawem problemów i zagrożeń, których większość programistów nie pojmuje w pełni, dopóki problemy takie jak ten problem nie pojawią się i nie zmylą ludzi i nie zaprzeczy całemu powodowi posiadania prywatnych członków.

Ten „dziwny i zwariowany” aspekt C # to jeszcze jeden powód, dla którego dobre programowanie nie ma nic wspólnego z doświadczeniem i umiejętnościami, a jedynie znajomość sztuczek i pułapek… jak praca przy samochodzie. To argument, że zasady miały zostać złamane, co jest bardzo złym modelem dla każdego języka komputerowego.

Stokely
źródło
-1

Wydaje mi się, że gdyby dane były prywatne dla innych instancji tego samego typu, niekoniecznie byłyby już tego samego typu. Wydaje się, że nie zachowuje się ani nie działa tak samo jak inne instancje. Zachowanie można łatwo zmodyfikować na podstawie tych prywatnych danych wewnętrznych. Moim zdaniem to po prostu wywołałoby zamieszanie.

Mówiąc luźno, osobiście uważam, że pisanie klas wywodzących się z klasy bazowej oferuje podobną funkcjonalność, którą opisujesz jako „posiadanie prywatnych danych na instancję”. Zamiast tego masz po prostu nową definicję klasy dla „unikalnego” typu.

C Johnson
źródło