Dlaczego członek prywatny jest dostępny metodą statyczną?

25

Poniżej znajduje się pseudo kod, próbowałem go w Javie i PHP i oba działały:

class Test { 

    private int a = 5;

    public static function do_test(){
        var t = new Test();
        t.a = 1;
        print t.a // 1
    }

}

Test::do_test();

Dlaczego możesz to zrobić w paradygmacie OOP i jaki jest z tego pożytek?

Ben
źródło
6
Dlaczego by nie miał W Javie członkowie prywatni nie są prywatni dla instancji, ale raczej prywatni dla pliku źródłowego . Najpierw przychodzi mi na myśl, equalsże trzeba sprawdzić prywatne pola innej instancji. (Publikowanie jako komentarz, ponieważ jest krótki, i nic o OOP-ności tego podejścia)
Ordous
2
Zauważ, że metody statyczne nie mają a this, więc jedynymi obiektami własnej klasy, do których mogą się dostać, są te, które sami tworzą (lub które są przekazywane jako parametr). Więc jeśli uważasz to za naruszenie enkapsulacji lub dziury w zabezpieczeniach, to nie jest tak, jakby było bardzo duże i może nie być warte zatkania.
Kilian Foth,
4
Nie jestem pewien, dlaczego to zostało odrzucone. Pytanie może być trywialne (ish), ale OP zadał sobie trud przetestowania zachowania w dwóch językach. To o wiele więcej wysiłku, niż zwykle widzą nowi gracze.
yannis
1
@YannisRizos Zgoda, a tak naprawdę nie sądzę, aby pytanie było banalne. Ma to wpływ na przestrzeganie „zasady najmniejszych przywilejów”. Oznacza to, że funkcje pomocnicze, które nie potrzebują dostępu do wewnętrznych elementów instancji, powinny być zdefiniowane w osobnej klasie, i odwrotnie, gdy przestrzegana będzie ta konwencja, będziesz wiedział, że ilekroć w tej samej klasie istnieje metoda statyczna, uzyskuje ona dostęp do stanu wewnętrznego.
Doval,
1
W rzeczywistości, kiedy zapytałem moich kolegów, wszyscy powiedzieli, że to niemożliwe. Dlatego nie sądziłem, że to banalne
Ben

Odpowiedzi:

17

W Javie zmienne prywatne są widoczne dla całej klasy. Można do nich uzyskać dostęp za pomocą metod statycznych i innych instancji tej samej klasy.

Jest to na przykład przydatne w metodach fabrycznych . Metoda fabryczna zwykle dokonuje inicjalizacji obiektu, który jest tak złożony, że nie chcesz pozostawiać go kodowi aplikacji. Aby dokonać inicjalizacji, metoda fabryczna często potrzebuje dostępu do wewnętrznych elementów klasy, których nie chcesz ujawniać. Możliwość bezpośredniego dostępu do zmiennych prywatnych znacznie ułatwia życie.

Jednak jeśli chcesz ukryć szczegóły implementacji klasy nawet przed metodami statycznymi lub przed innymi instancjami tej klasy, możesz zastosować wzorzec danych klasy prywatnej . Umieść wszystkie prywatne zmienne klasy w prywatnej klasie wewnętrznej i deleguj dowolne moduły pobierające lub ustawiające do modułów pobierających i ustawiających tej klasy wewnętrznej.

Inną opcją jest zdefiniowanie interfejsu dla klasy, który deklaruje wszystkie publiczne metody klasy, a następnie odwołuje się tylko do klasy w ramach tego interfejsu, o ile to możliwe. Odwołanie do typu interfejsu nie może być użyte do bezpośredniego dostępu do wszystkiego, co nie zostało zadeklarowane w interfejsie, bez względu na to, gdzie (z wyjątkiem oczywiście refleksji). Gdy używasz zorientowanego obiektowo języka programowania, który nie ma interfejsów (na przykład C ++), można je symulować za pomocą abstrakcyjnej klasy podstawowej, która jest dziedziczona przez rzeczywistą klasę.

interface ITest {
     public int getA();
}

class Test implements ITest { 

    private int a = 5;

    public int getA() { return a; } // implementation of method declared in interface

    public static void main(){
        ITest t = new Test();
        t.a = 1; // syntax error: Interface ITest has no "a"
        System.out.println(t.getA()); // calls Test.getA, visible because ITest declares it
    }

}
Philipp
źródło
Czy możesz pomyśleć o sytuacji, w której wzorzec danych klasy prywatnej jest przydatny? Osobiście używam klas wewnętrznych tylko w GUI, takich jak konfiguracje (Swing itp.) Lub statycznych klas wewnętrznych w ćwiczeniach kodowania, ponieważ nie chcę, aby ćwiczenie obejmowało wiele plików źródłowych.
InformedA
1
Ukrywanie wewnętrznych elementów klasy przed innymi instancjami odbiera jedną z zalet, jakie mają klasy nad interfejsami, nie odzyskując niczego w zamian. Prostszym i bardziej elastycznym rozwiązaniem jest po prostu użycie interfejsu.
Doval
3

Niektóre języki i środowiska wykonawcze (np. Java, .NET) przyjmują założenie, że każdemu, kto kompiluje kod dla określonej klasy, można ufać, że nie użyje żadnych prywatnych członków żadnej instancji tej klasy w sposób, który byłby szkodliwy dla jego poprawności operacja. Inne języki i frameworki są pod tym względem bardziej restrykcyjne i nie pozwalają na dostęp do prywatnych członków instancji, z wyjątkiem kodu działającego w tej instancji . Oba projekty mają zalety i wady.

Największą zaletą zezwalania każdemu kodowi w klasie na dostęp do prywatnych członków dowolnej instancji jest to, że istnieją przypadki, w których ten poziom dostępu jest odpowiedni, a privatepraca w ten sposób eliminuje potrzebę posiadania innego kwalifikatora dostępu do tego celu lub inaczej zmusi kod do ujawnienia członków w szerszym zakresie niż byłoby to idealne.

Zaletą uniemożliwienia takiego dostępu (jak miało to miejsce w przypadku modelu Microsoft Common Object Model (COM)) jest to, że pozwala on kodowi zewnętrznemu traktować klasy jako interfejsy. Jeśli grupa ImmutableMatrixzawiera prywatne lub chronione double[][]pole oporowe i jeśli kod w klasie bada tablicę podkładową innych przypadkach, to nie jest możliwe określenie klasy nie tablicy oparciem (na przykład ZeroMatrix, IdentityMatrix), który może wykorzystać kod zewnętrzny w i Immutable2dMatrixbez konieczności uwzględnienia przez tę klasę pola zaplecza. Jeśli nic wewnątrz nie Immutable2dMatrixkorzysta z prywatnych członków żadnej instancji innej niż this, wówczas można by zmienić nazwę klasy na ImmutableArrayBackedMatrixi zdefiniować nową ImmutableMatrixklasę abstrakcyjną, która mogłaby mieć, ImmutableArrayBackedMatrixpodobnie jak wyżej wymienione klasy nieobsługiwane przez tablice, jako podtypy.

Zauważ, że takiemu refaktoryzacji nie można zapobiec, jeśli język „zezwoli” ImmutableMatrixna sprawdzenie tablicy thispomocniczej dla instancji innych niż , chyba że język skorzystałby z tej możliwości i faktycznie zbadał instancje zewnętrzne. Podstawowym efektem ograniczenia użycia takiego języka jest to, że kompilator natychmiast skrzęczy przy każdej próbie napisania kodu, który nie byłby podatny na takie refaktoryzowanie.

supercat
źródło
2

Java nie jest językiem zorientowanym obiektowo, ale językiem opartym na klasach - klasa określa dostęp do operacji i zachowania, a nie instancję.

Więc nie bądź zbyt zaskoczony, że pozwala ci robić rzeczy, które nie są ściśle obiektowe.

Ponieważ metoda ma ten sam zakres klasy co instancja, ma pełny dostęp do prywatnych członków. Podobne reguły dotyczą instancji klas wewnętrznych uzyskujących dostęp do danych z instancji klas zewnętrznych - instancja klasy wewnętrznej może uzyskiwać dostęp do prywatnych członków klasy zewnętrznej.

Jest to dziedziczone z C ++, gdzie jest przydatne do tworzenia konstruktorów kopiowania i przenoszenia. Jest także przydatny do porównywania lub łączenia dwóch obiektów, których wartość zależy od elementów, które nie są publicznie dostępne w efektywny sposób (na przykład moduł pobierający tablicę w Javie powinien skopiować tablicę, aby kod klienta nie mógł jej zmodyfikować, zmieniając wewnętrzny stan obiektu, ale konieczność kopiowania tablic w celu porównania równości obiektów nie jest skuteczna)

Pete Kirkham
źródło