Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [php]Dziedziczenie a widoczność
Forum PHP.pl > Forum > PHP > Object-oriented programming
thek
Widocznie za długo w C++ siedziałem. Tam konstruktor, destruktor i zmienne oraz metody prywatne nie podlegają dziedziczeniu, by było to możliwe muszą być ustawione jako protected i tak samo jest w PHP. Widocznie dla konstruktorów i destruktorów zostało to zmienione. Co według mnie jest ułatwieniem, ale i utrudnieniem. Dlaczego? Ponieważ kontruktor ma dostęp do wszystkich atrybutów. Weź taki przykład, że konstruktor rodzica inicjalizuje zmienną prywatną, która nie jest dostępna w klasie potomnej. To jest ewidentny błąd do jakiego nie powinno się dopuścić, bo jak można zainicjalizować w klasie pochodnej zmienną prywatną, której klasa pochodna przecież już nie posiada? I dlatego moim zdaniem PHP powinno na brak konstruktora reagowac conajmniej rzuceniem E_WARNINGa oraz powinien ZAWSZE być on tworzony w klasie pochodnej, bo nigdy nie mamy pewności bez sięgania w kod klasy rodzica co robi jego konstruktor. Tak się bawić, że wywołujemy konstrutor rodzica jawnie poprzez parent:: możemy gdy wiemy wszystko o nim i mamy świadomość tego, że nic nie zrobimy głupiego. Pod tym względem właśnie zarówno Java jak i C++ są logiczniejsze, bo nie mogą dopuścić do takiego, jakby nie spojrzec, idiotyzmu, żeby klasa próbowała inicjalizować nieistniejący atrybut smile.gif To, że większość osób korzysta z tego ułatwienia świadczy tylko o takiej popularności tego, że nie chce się tego usuwać z racji zachowania kompatybilności wstecznej.

Stąd też w PHP konstruktory zawsze tworzę. Choćby po to by wywołać w nim konstruktor rodzica jawnie, ze świadomością tego co robię. Co do uwagi w nawiasie to chodzi mi o to, że jeśli czegoś nie ma normalnie zdefiniowanego jako klasa, a jest tworzone jako obiekt, to php skonwertuje to do obiektu stdClass. Miałem tu na myśli wszelkiego rodzaju rzutowania do obiektu, czyli coś w stylu (object) $jakas_zmienna gdyż nie mamy tak naprawdę nic wtedy do oparcia się. W przypadku zdefiniowanej klasy po prostu utworzy jej instancję zgodnie z tym co ma w definicji klasy.

Z tego co wiem o refleksji to trudno tutaj mówić o jakimś dziedziczeniu. Refleksja to IMHO trochę inna wersja dobierania sie do różnych klas, z wykorzystanem której mogę sie dowiedzieć niemal wszystkiego o nich. Czy użyję refleksji na tej samej klasie czy na innej to już niewielka różnica. Zresztą sama dokumentacja nazywa ten mechanizm czymś w rodzaju inżynierii wstecznej. Dlatego trudno mówić by był to mechanizm zarezerwowany tylko dla klas dziedziczących między sobą. Równie dobrze mogę się dobrać z jej pomocą do klas kompletnie niepowiązanych i wywołać ich metody. Bo nic mi nie zabroni wywołać refleksją dowolną metodę dowolnej klasy. Zmienne i metody prywatne więc nie dziedziczą się. Wywołanie ich poprzez refleksję jest zwyczajnym "oszustwem" na jakie pozwala język.
-=Peter=-
Cytat
Zmienne i metody prywatne więc nie dziedziczą się.

Bzdura, a o to dowód (bez wykorzystania refleksji):

  1. class C{
  2. private $property = 'value';
  3.  
  4. public function echoPrivateProperty(A $object){
  5. echo $object->property;
  6. }
  7. }
  8.  
  9. class B extends C{}
  10. class A extends B{}
  11.  
  12. $c = new C();
  13. $a = new A();
  14. $c->echoPrivateProperty($a);//value
thek
Pozwól, że poszerzę Twój przykład by pokazać IMHO głupotę PHP...
  1. <?php
  2. class C{
  3. private $property = 'valueC';
  4. public function echoPrivateProperty(A $object){
  5. echo $object->property;
  6. }
  7. }
  8.  
  9. class B extends C{
  10. private $property = 'valueB';
  11. }
  12.  
  13. class A extends B{
  14. private $property = 'valueA';
  15. }
  16.  
  17. $c = new C();
  18. $b = new B();
  19. $a = new A();
  20. $c->echoPrivateProperty($a);// valueC
  21. $b->echoPrivateProperty($a);// valueC
  22. $a->echoPrivateProperty($a);// valueC
  23. var_dump($c);// object(C)#1 (1) { ["property":"C":private]=> string(6) "valueC" }
  24. var_dump($b);// object(B)#2 (2) { ["property":"B":private]=> string(6) "valueB" ["property":"C":private]=> string(6) "valueC" }
  25. var_dump($a);// object(A)#3 (3) { ["property":"A":private]=> string(6) "valueA" ["property":"B":private]=> string(6) "valueB" ["property":"C":private]=> string(6) "valueC" }
  26. ?>
Powiedz mi jedno... Dlaczego dziedziczona metoda echoPrivateProperty zgłupiała i ZAWSZE pokazuje valueC, mimo, że w klasie A oraz B a także C mam własność $property ustawioną na, odpowiednio 'valueA', 'valueB', 'valueC' i niezależnie od obiektu wywołujacego powinna być pokazana, wedle tego co widnieje w ciele metody, wartość 'valueA' ? W końcu mam taką w klasie A... Wersja PHP 5.3.3 smile.gif Idziemy dalej... Modyfikujemy sobie nieco klasę B do postaci:
  1. class B extends C{
  2. private $property = 'valueB';
  3. public function echoPrivateProperty(A $object){
  4. echo $object->property;
  5. }
  6. }
i co dostajemy?
  1. $c->echoPrivateProperty($a);// valueC
  2. $b->echoPrivateProperty($a);// valueB
  3. $a->echoPrivateProperty($a);// valueB
W efekcie mamy jeszcze większe zaciemnienie, ponieważ teraz metodę dziadka przesłoniliśmy metodą rodzica. Jaki z tego morał? Metoda klasy przodka wywoła odwołanie do "swoich" danych utworzonych niejawnie podczas tworzenia obiektu pochodnego. Utworzy więc coś na kształt jego instancji, którą bardzo ładnie widać w var_dumpie i każda metoda przodka, jeśli tylko nie jest przesłonięta, będzie się do "instancji" tego przodka odnosić biggrin.gif Bardzo dobrze to widać poprzez dodanie do wszystkich klas zmiennej prywatnej o tej samej nazwie. Ciało metody tak napisanej powinno użyć pola $property obiektu przekazanego w parametrze, czyli wyświetlić 'valueA'... Nie robi jednak tego ale odwołuje się do wartości prywatnej obiektu klasy, która ową metodę definiuje lub przesłania biggrin.gif To jest po prostu głupota maksymalna, która stoi w jawnej sprzeczności z logiką tego co stoi jak wół w metodzie biggrin.gif

Jaki jest morał numer 2? Nie ma w PHP czegoś takiego jak mówimy oboje, ale zupełnie inny mechanizm, który w ciele klasy pochodnej "w tle" odpala sobie konstruktor przodków począwszy od "roota" i stąd ma dostęp do właściwych mu pól przodków oraz dopasowuje metody do takich jakie zna począwszy od "roota", ewentualnie je nadpisując.

PS.: Zapomniałbym zwrócić Ci uwagę na przykład drugi, gdzie przysłaniałem metodę... Popatrz do jakiej sytuacji doprowadziłem. Obiekt C wywołuje metodę klasy A, która jest już przeslonięta przez klasę B. Na logikę więc patrząc całość kompletnie nie trzyma się kupy. Widząc zwracane wartości można dojść do wniosku, że wywoływany jest nie A $object->property, ale $this->property klasy ową metodę implementujący lub nadpisujący. Jako że C nie ma przodka, to wywoła ją dla siebie, ale B ją nadpisuje, więc wywoła ją dla swojej $property. Z kolei A nie ma metody nadpisującej, więc przejmuje ją z obiektu rodzica, czyli B i to jego $property wyświetla. Cuda na kiju, bo logiki to się żadnej nie trzyma. To dlatego też metoda ta wyświetlała zawsze 'valueC', ponieważ wcześniej jedynie klasa C ją implementowała. A że ani B ani A nie nadpisywały to wszystkie wyświetliły $property klasy C, która tę metodę wprowadziła. Zwyczajnie niedopatrzenie lub jakaś pokrętna logika. Dla mnie to pogwałcenie zasad obiektówki i coś, co uznaję zwyczajnie za błąd, odstępstwo od niej.

Dla mnie to co w tym momencie zrobiło PHP jest nieakceptowalne. Kompletny galimatias, który nie powinien mieć miejsca. W przypadku bowiem gdyby to działało jak powinno, w Twojej wersji kodu -bez przeróbek, wywołanie $c->echoPrivateProperty($a); powinno zwrocić "Attribute $property doesn't exists in class A". Tak więc to nie jest jakieś "cudowne" dziedziczenie własności prywatnych po przodku tylko jakieś chamskie i na pałę dzialanie, które z dziedziczeniem nie ma nic wspólnego. To jakiś idiotyzm, który pewnie twórcy języka nieświadomie stworzyli. Dla mnie to błąd poważny, ponieważ zauważ co robi z pamięcią. Jeśli każda klasa poprzednia jest duża, to dziedzicząc łańcuchem po kilku i tworząc obiekt ostatniej w pamięci mamy po prostu horror zależności metod i obiekt-kolos. Nie wiem jak Ty, ale dla mnie takie rozwiązanie nie jest akceptowalne jako prawidłowe. To jakaś próba obejścia dostępu do zmiennych, do których nie powinno się miec dostępu i złamanie enkapsulacji najprościej mówiąc. Nie wiem kto to wymyślił, kto to impleentował, ale według mnie należą mu się ostre baty.
-=Peter=-
@thek - to się nazywa po prostu... hermetyzacją. Jakby było inaczej, to by w sposób jawny można by było dostać się do składowych prywatnych nadklasy definiując prywatną (lub też nawet publiczną, wtedy cała hermetyzacja poszła by się ...) składową w podklasie o tej samej nazwie - wtedy modyfikatory dostępu praktycznie straciły by sens istnienia winksmiley.jpg Wg mnie to nie jest błąd, a jedno z najrozsądniejszych z możliwych zachowań. Jakby było tak jak mówisz, to była by to luka krytyczna w zabezpieczeniach php.

Edit:
To nie jest łamanie hermetyzacji w żadnym wypadku, zobacz przykład:

  1. class C{
  2. private $property = 'value';
  3.  
  4. public function echoPrivateProperty(A $object){
  5. echo $object->property;
  6. }
  7. }
  8.  
  9. class B extends C{}
  10. class A extends B{}
  11.  
  12. $c = new C();
  13. $a = new A();
  14. $c->echoPrivateProperty($a);//value


To jest ok, bo składowa prywatna $property należy do klasy C, a metoda echoPrivateProperty również jest zdefiniowana w klasie C. Modyfikator prywatny sprawia, że składowa lub metoda jest widoczna tylko z klasy (nie obiektu!), w której ten element jest zdefiniowany.

To zaś nie działa, było by to złamaniem zasady hermetyzacji i modyfikatora dostępu private:
  1. class C{
  2. private $property = 'value';
  3. }
  4.  
  5. class B extends C{
  6. public function echoPrivateProperty(A $object){
  7. echo $object->property;
  8. }
  9. }
  10. class A extends B{}
  11.  
  12. $b = new B();
  13. $a = new A();
  14. $b->echoPrivateProperty($a);//składowa $property nie istnieje


Np. w javie jest dokładnie tak samo, to nie twórcy php sobie takie działanie wymyślili.
thek
Zagalopowałem się... Zapomniałem, że tam jest private. Moja wina i niedopatrzene z tym 'valueA' smile.gif To co dla mnie jest dalej niezrozumiałe to "łańcuch spadku", czyli gdy w klasie A nie może się do niej dobrać (nie istnieje lub nie ma praw dostępu) to sięga i szuka tej własności w przodkach, aż do momentu gdzie ta metoda jest definiowana/nadpisywana. Czyli w tym wypadku zwróci jak napisałeś, ale dodaj do B własność $property i nagle wartość ustawi się na 'valueB'. Podobnie było w Twoim pierwszym poście, tyle, że tam metoda była w klasie C a więc z braku uprawnień zarówno klasa A jak iB nie zezwalały na odczyt $property i ostatecznie "łańcuchem" metodzie udało się uzyskać dostęp do $property w C, która to już dla niej była dostępna
  1. class C{
  2. private $property = 'valueC';
  3. }
  4.  
  5. class B extends C{
  6. private $property = 'valueB';
  7. public function echoPrivateProperty(A $object){
  8. echo $object->property;
  9. }
  10. }
  11. class A extends B{}
  12.  
  13. $b = new B();
  14. $a = new A();
  15. $b->echoPrivateProperty($a);// valueB
Jeszcze ciekawiej jest, gdy w klasie B zdefiniujemy $property jako public, a metodę przepchniemy do C winksmiley.jpg
  1. class C{
  2. private $property = 'valueC';
  3. public function echoPrivateProperty(A $object){
  4. echo $object->property;
  5. }
  6. }
  7.  
  8. class B extends C{
  9. public $property = 'valueB';
  10. }
  11. class A extends B{
  12. }
  13. $c = new C();
  14. $b = new B();
  15. $a = new A();
  16. $c->echoPrivateProperty($a);// valueC (!)
  17. var_dump($a);//object(A)#3 (2) { ["property"]=> string(6) "valueB" ["property":"C":private]=> string(6) "valueC" }
Zauważ, że $property jest publiczna, a i tak metoda wywołana ignoruje to, oraz fakt dziedziczenia po B, gdzie jest ona także publiczna. Sięga od razu po 'valueC'. Czemu? Ma przecież do podejrzenia własności prawo. Nie łamie żadnych praw dostępu, a mimo to pomija ją, choć nie ma przeciwwskazań by wyświetlić 'valueB'. Coraz bardziej się chyba w tym gubię teraz, a może to po prostu ta godzina i już nie myślę, podobnie jak w ostatnim poście nie zauważyłem, że było private $property smile.gif Bo uznaję swój błąd w poprzednim poście - nie zwróciłem uwagi na specyfikator dostępu. Ale tutaj mam jak byk -> public, więc każdy ma swobodny dostęp do własności, choć jak widać nie jest to zupełnie brane pod uwagę. Metoda zerka do obiektu klasy A, ale i tak dane wyświetla z obiektu własnej klasy, czyli nie robi nic innego niż zamianę $object na $this. Co ciekawe, jeśli w powyższym kodzie obiektem wywołującym metodę będzie $b lub $a jak poniżej:
  1. $b->echoPrivateProperty($a);

to także zwróci nam 'valueC' w obu wypadkach. Innymi słowy znowu wywoła $property z klasy, gdzie metoda jest zdefiniowana, mimo, że ma public ustawione, a nie private. Zrobiłem na wszelki wypadek to samo co w poście powyżej i nadpisałem w klasie B metodę.... Jej wywołanie dla $a i $b zwraca 'valueB', a dla $c 'valueC', czyli tutaj najprawdopodobniej też coś nie tak jest do końca z valueC, bo gdy do klasy A dodam jawnie public $property = 'valueA' to mam prawidłowo znów tylko dla $a i $b, ale wywołanie w $c usilnie wstawia 'valueC'. Czyli wygląda na to, że są jakieś dziwne zawirowania w klasie przodka, która daną metodę implementuję.

W ramach tetów zrobilem jeszcze wariant, gdzie metoda odczytu jest tylko w klasie C, ale $property tylko w C jest private, zaś w pozostałych public... i w efekcie wszystkie obiekty zwróciły mi 'valueC'. To się robi coraz bardziej zagmatwane. Po tych kilku wariantach zaczynam sie powaznie zastanawiać jak to w końcu jest z ustawianiem dostępu jeśli dziedziczymy i w "roocie" bawimy się z "liściem", bo powyższy przykład powinien dać rezultat w postaci dopuszczenia do odczytu wlasności, ale tak się nie dzieje mimo wszystko i obawiam się, że coś staje okoniem przy samej funkcji, ponieważ echo $a->property zwraca 'valueA' tak jak powinno, czyli dostęp jest publiczny i nic nie powinno stać na przeszkodzie, by metoda dziadka wyswietliła wartość wnuczka.
Crozin
@thek: Nie byłem w stanie już doczytać Twojego ostatniego posta. Mam dwie prośby: 1. Nie nazywaj morałem tego co jest obserwacją. Morał to podał @-=Peter=- pisząc Modyfikator prywatny sprawia, że składowa lub metoda jest widoczna tylko z klasy (nie obiektu!), w której ten element jest zdefiniowany.; 2. Nie opisuj na trzy akapity tego co jasno wynika (co da się łatwo zaobserwować samemu) z załączonych wcześniej listingów kodu (Twojego czy kogoś innego). Jedynie zniechęcasz do czytania wątków taką bezwartościową papką, którą niestety wypada przeczytać, bo może jednak coś sensownego jest tam napisane.

1. Nie porównujcie "możliwości" PHP do C++ czy Javy, bo jest to język o wiele bardziej dynamiczny. Stąd też kod, który w tych dwóch ostatnich by się nawet nie skompilował jest w PHP dopuszczalny.
2. Mechanizmy refleksji są naprawdę potężne. W takiej Javie przykładowo można przy ich pomocy zmienić zawartość obiektu String (który dla przypomnienia jest niezmienny (immutable), co jest zawsze mocno podkreślane), w PHP można też zrobić dziwne rzeczy przy jego pomocy.
thek
OK... Nie będę opisywal mocno tego co z moich obserwacji wynikało jeśli kod i komentarze powiedzą wstarczająco dużo. Pomijając akapity tekstu i moją ewidentną wpadkę z private ( mózg w nocy potrafi płatać figle ), nadal pewne rzeczy są dla mnie dziwne. Nawet nie chodzi o użycie obiektu wnuka w klasie dziadka, do czego trudno zmusić języki pokroju C++ czy Java (choć można próbować - deklaracja przed definicją). Najbardziej olanie modyfikatora publicznego klasy pochodnej by sięgnąć do własności prywatnej własnej klasy, co jest raczej niezwykłe. Ale i przechowywanie "instancji" klas przodków jeśli własność przodka jest prywatna też jest dla mnie dziwne, bo prowadzić może z czasem do znacznego obciążenia pamięci nadmiarową ilością danych. Skoro programista świadomie używa private zamiast protected, to chyba zależy mu na tym, by jednak własność ta nie była dziedziczona dalej.
Noidea
Cytat("thek")
Skoro programista świadomie używa private zamiast protected, to chyba zależy mu na tym, by jednak własność ta nie była dziedziczona dalej.


Tak, ale właściwości prywatne mogą być używane w metodach publicznych, które już są dziedziczone.
W twoim rozumowaniu foo() i bar() z poniższego kodu nie mają prawa działać, bo nie mają dostępu do prywatnej, nieodziedziczonej właściwości $prop
  1. <?php
  2.  
  3. class B
  4. {
  5. private $prop = "test";
  6.  
  7. public function foo()
  8. {
  9. echo $this->prop;
  10. echo " foo\n";
  11. }
  12.  
  13. public function bar()
  14. {
  15. echo $this->prop;
  16. echo " bar\n";
  17. }
  18. }
  19.  
  20. class A extends B
  21. {
  22. public function foo()
  23. {
  24. echo "overridden ";
  25. parent::foo();
  26. }
  27. }
  28.  
  29. $obj = new A();
  30. $obj->foo();
  31. $obj->bar();
  32.  
  33. var_dump( $obj );
  34.  
  35. ?>
thek
I właśnie dlatego ta konstrukcja jest dla mnie bezsensowa smile.gif Jeśli bowiem planujemy możliwość, iż klasa będzie korzystać z danych prywatnych oraz może być dziedziczona, to dajemy jej protected. Jeśli zaś nie chcemy dziedziczenia to ustawiamy final. Może to dość surowe podejście, ale moim zdaniem taki brak ścisłego określenia co można a co nie, tylko "a co mi tam, potraktuję wybiórczo trzymanie się obiektówki by mi było wygodnie" jest trochę niepoważne. Jeśli implementujemy zasady obiektowości w języku to trzymajmy się ich. I nie twórzmy możliwości dziwnych bo "to może się przydać w jakiejś sytuacji". Z czasem może to mieć nieoczekiwane konsekwencje i będziemy musieli łatać od w nieoczekiwanym miejscu by go zabezpieczyć. Sam niedawno się z czymś takim zetknąłem gdy klient mi powiedział o nietypowym zachowaniu TinyMCE, o którym możliwe iż nikt jeszcze w necie nie napisał, a związanym z tworzeniem linków. W określonej sytuacji w trybie graficznym znacznikiem linka może się objąć znak łamania linii, czyli <br> co samo w sobie jest dość niezwykłe, a link taki nie jest widoczny inaczej niż poprzez podgląd źródła strony smile.gif.
Crozin
@thek: nie ma czegoś takiego jak "zasady obiektówki". Każdy język ma prawdo działać w dowolny sposób i realizować pewne zagadnienia jak mu się podoba. Przy czym powinien w swojej dokumentacji jasno określać jak działa.

Ostatni przykład podany przez @Noidea jest jak najbardziej normalny i logiczny: http://ideone.com/kYQWZ
-=Peter=-
Metody i właściwości prywatne są dziedziczone i to jest prawidłowe działanie, bo w przeciwnym wypadku była by złamana zasada hermetyzacji. Dlaczego? Bo jeśli odziedziczone metody chronione i publiczne mają nie działać, gdyż operują na prywatnych danych nadklasy, to jest zdradzany sposób implementacji tych metod. @thek z całym szacunkiem, ale masz błędne wyobrażenie o modyfikatorach dostępu i ich przeznaczenia nie rozumiesz w pełni.

Odsyłam do: http://www.php.net/manual/en/language.oop5.visibility.php

Tam nie ma ani słowa o tym, że składowe prywatne nie są dziedziczone, tam jedynie jest mowa o dostępności, gdyż private to jeden z 3 modyfikatorów dostępu. Tam masz również napisane, dlaczego mój pierwszy przykład działa i nie jest pogwałceniem zasady hermetyzacji.

Jeśli chodzi o Twój przykład zmiany dostępności prywatnej składowej nadklasy na publiczny dostęp w podklasie i Twoje zdziwienie, że takie coś nie przechodzi - to się powtórzę, jest to tak zwana enkapsulacja aka hermetyzacja. Jakby możliwe by było zmienianie dostępu z prywatnego na publiczny, to można by było się dostać do implementacji nadklasy, którą autor chciał ukryć, aby w przyszłości móc przepisać całkowicie implementację tej klasy (np. zmienić algorytm na bardziej optymalny przy okazji wywalając jakąś prywatną składową, dodając 20 metod prywatnych, wywalając 5 itp), zachowując interfejs (składowe i metody publiczne jak i chronione + ewentualnie metody do serializacji).

Już raczej w tym temacie nie mam nic do dodania, a nawet przeciwnie - trochę za dużo oczywistych oczywistości tutaj opisałem.
thek
W takim wypadku za dużo siedziałem w C++ i z jego pryzmatu to pojmowałem. Tam private nie podlega dziedziczeniu. Jego rolę przejmuje protected, który na poziomie klasy jest widoczny jako private, ale klasa pochodna, w odróżnieniu od private, własność tę dziedziczy. Nie pamiętam tylko czy w klasie pochodnej stawała się public, czy private (chyba to pierwsze).

@Crozin: Co do przykładu Noidea to jak najbatrdziej dla mnie też jest logiczny i dla mnie to zachowanie jest ok. W końcu po to mamy parent, by móc sie odwołać do klasy rodzica (co powinno stworzyć jego "tymczasowy obiekt" w klasie potomka). Ale popatrz na mój ostatni kod. Tam $property w klasie A odziedziczone jest z B, gdzie otwarcie walnąłem je jako public i tak też zostala w klasie A odziedziczona, a więc wywołanie w obiekcie klasy C metody mającej ją pokazać powinno to zrobić. Tymczasem nie robi tego. Var_dump wyraźnie pokazuje, że $property jest public i wywołanie $object->property w obiekcie $c ma prawo wyświetlić ją. Tymczasem wyświetla prywatną wartość siebie, bo cofa się po rodzicach i, mimo iż w klasie B również ta własność jest public, ją olewa, aż dochodzi do własnej klasy, gdzie jako metoda wewnętrzna ma dostęp do własnego private i może ją wyświetlić. Rozumiem, gdyby to było private, bo klasa broni dostępu, ale public? Od kiedy dostęp do własnosci publicznej jest w jakikolwiek sposób ograniczany?

@Peter: Nie można by sie było dostać do implementacji. Co z tego, że w klasie pochodnej jest on już public? W klasie rodzica nadal jest private i tyle. Rodzic ma prawo zaprotestować gdyby mu się dzieciak do bebechów wbijał. Podam przykład bardziej obrazowy. Jestem sobie ja i mam na kompie wzorzec ustawień konta z hasłem i tworzę jego instancję dla siebie. Córce zakładam wedug wzorca tego samego jaki posiadam, ale usuwam hasło, dzieki czemu jej konto staje sie publiczne. Pytanie teraz, czy ma dostęp do mojego konta? Nie.
Crozin
@thek: jest to poprawne działanie. Jeszcze raz: http://www.php.net/manual/en/language.oop5.visibility.php - sekcja: Visibility from other objects. Jak widzisz modyfikatory dostępu nie odnoszą się do obiektu, a do klasy w której zostały użyte. Ich przyzwolenie na wywołanie czegoś również opiera się na bloku klasy, a nie używanym obiekcie.

Cytat
[...] (co powinno stworzyć jego "tymczasowy obiekt" w klasie potomka).
Raczej tymczasowo rzutować aktualny obiekt, na obiekt klasy rodzica - raczej na taką implementację parenta bym stawiał.
Noidea
@thek; Twój przykład z hasłami jest średnio trafiony, ponieważ modyfikatory dostępu nie mają zabezpieczać tajnych danych przed niecnymi programistami. Niecni programiści jak będą chcieli, to i tak będą ci grzebali w prywatnych właściwościach chociażby przy pomocy refleksji. Modyfikatory te służą tylko rozróżnieniu elementów interfejsu klasy, od elementów implementacji, którymi programista nie musi sobie zawracać głowy, żeby twojej klasy móc używać. Dlatego poniższy kod, pomimo ze wydaje ci się dziwny, jest całkowicie logiczny (oba obiekty są tej samej klasy, więc oba znają swoją implementację, więc nie muszą jej przed sobą ukrywać):
  1. <?php
  2.  
  3. class A
  4. {
  5. private $var;
  6.  
  7. public function __construct( $var = "" )
  8. {
  9. $this->var = $var;
  10. }
  11.  
  12. public function foo( A $obj )
  13. {
  14. echo "Ha! Mam dostęp do zmiennych prywatnych obiektu przekazanego jako " . $obj->var . " metody.";
  15. }
  16. }
  17.  
  18. $testObj = new A();
  19. $testObj->foo( new A( "argument" ) );
  20.  
  21. ?>



Cytat("thek")
Ale popatrz na mój ostatni kod. Tam $property w klasie A odziedziczone jest z B, gdzie otwarcie walnąłem je jako public i tak też zostala w klasie A odziedziczona, a więc wywołanie w obiekcie klasy C metody mającej ją pokazać powinno to zrobić. Tymczasem nie robi tego. Var_dump wyraźnie pokazuje, że $property jest public i wywołanie $object->property w obiekcie $c ma prawo wyświetlić ją. Tymczasem wyświetla prywatną wartość siebie, bo cofa się po rodzicach i, mimo iż w klasie B również ta własność jest public, ją olewa, aż dochodzi do własnej klasy, gdzie jako metoda wewnętrzna ma dostęp do własnego private i może ją wyświetlić.

---
KOD:
  1. class C{
  2. private $property = 'valueC';
  3. public function echoPrivateProperty(A $object){
  4. echo $object->property;
  5. }
  6. }
  7.  
  8. class B extends C{
  9. public $property = 'valueB';
  10. }
  11. class A extends B{
  12. }
  13. $c = new C();
  14. $b = new B();
  15. $a = new A();
  16. $c->echoPrivateProperty($a);// valueC (!)
  17. var_dump($a);//object(A)#3 (2) { ["property"]=> string(6) "valueB" ["property":"C":private]=> string(6) "valueC" }

Tutaj muszę przyznać, że również zastanawiałem się dlaczego ten kod działa tak a nie inaczej. Doszedłem w końcu do wniosku, że nie ma tutaj "cofania się po rodzicach" (na co na pierwszy rzut oka wygląda), tylko jest "przechodzenie po potomkach". I działa to na takiej zasadzie, że bierze się najstarszego rodzica, który posiada właściwość $property dostępną dla metody echoPrivateProperty(), a następnie przechodzi po potomkach sprawdzając, czy przeciążają one tą właściwość. A jako że w twoim kodzie:
a.) metoda echoPrivateProperty() ma dostęp do prywatnej właściwości swojej klasy
b.) prywatnych właściwości nie można przeciążać
to wyświetla się właśnie valueC

Jak dla mnie jest to: logicznie i nieintuicyjne smile.gif
Nie jest to natomiast dla mnie wielki problem, bo w językach w których programuję konwencje nazewnictwa wymagają pisania prywatnych właściwości małą literą, a publicznych dużą albo stosowania getterów/setterów - więc nazwa nigdy nie będzie taka sama. mało tego, nie wydaje mi się, żebym kiedykolwiek potrzebował w klasie metody, która jako argumentu wymaga określonego potomka tej klasy.
thek
@crozin. Sekcja ta odnosi się do faktu, że własności private i protected obiektów tej samej klasy są dostępne dla nich samych, mimo iż są różnymi instancjami, ale jednak wciąz tej samej klasy. Mamy jednak inną sytuację. Tutaj własność jest publiczna, choć różnych klas. Skoro własność klasy A jest public to mogę uzyskać jej wartość prostym $a->nazwa_wlasnosci (i metoda klasy C tak robi), a mimo to zobacz co się dzieje. Zamiast wyświetlić 'valueB' (odziedziczone po klasie cool.gif wywołanie metody zwraca, jak dla mnie, nieoczekiwanie 'valueC', czyli sobie 'gdzieś po drodze' konwertuje obiekt klasy A do obiektu klasy C (co powoduje zastąpienie $property = 'valueB' na $property = 'valueC') i dopiero wtedy dobiera do własności $property. W takim razie po co wymuszenie w metodzie echoPrivateProperty(A $object) klasy A jako parametr, skoro i tak jest wszystko ignorowane oraz konwertowane do klasy C? Zauważ, że już od jakiegoś czasu piszę, iż mamy do czynienia z własnością public, a nie z private czy protected. Jeśli tak jest źle (a według php taka sytuacja ma miejsce), to gdzie jest mój błąd rozumowania? I od kiedy public nie może zachować się w takiej sytuacji jak public? Zresztą bez strzępienia języka... Kod wyjaśni chyba wszystko:
  1. class C{
  2. private $property = 'valueC';
  3. public function echoPrivateProperty(A $object){
  4. var_dump( $object );
  5. echo $object->property;
  6. }
  7. }
  8.  
  9. class B extends C{
  10. public $property = 'valueB';
  11.  
  12. }
  13. class A extends B{
  14. }
  15. $c = new C();
  16. $b = new B();
  17. $a = new A();
  18. $c->echoPrivateProperty($a);
  19. //var_dump zwraca:
  20. //object(A)#3 (2) { ["property"]=> string(6) "valueB" ["property":"C":private]=> string(6) "valueC" }
  21. //$object->property zwraca:
  22. //valueC
Zwróć uwagę, że zrobiłem var_dump wewnątrz metody klasy C.
Obserwacja podsuwa wniosek, że widoczność jest tutaj więc bez znaczenia, albo jeśli klasa ma do wyboru kilka pól o tej samej nazwie, ale różnych klas, to wybierze pole własnej klasy. Gdzie tu sens i logika? Mam coś, a mimo to nie mam tego? Hmmm... To wygląda trochę jakby istaniał mechanizm podobny do przestrzeni nazw na obręb klasy własnej. A przynajniej takie można odnieść wrażenie.

@Noidea: Twoje wyjaśnienia wydają sie logiczne, ale niestety jedna rzecz jest irytująca dla programisty w związku z punktem 2) a mianowicie konieczność znajomości klas wszystkich przodków, bo może sie okazać, że przypadkowo użyliśmy jakiejś własności o tej samej nazwie w klasie potomnej, ale z innym specyfikatorem dostępu, co może mieć nieoczekiwane oraz niespodziewane konsekwencje podczas używania metod tychże przodków gdy używamy obiektów potomków jako parametrów. A zazwyczaj mało kto się tym przejmuje do momentu aż nie nastąpi jakaś "magia" winksmiley.jpg

PS: wiem, że przykład z hasłami jest taki sobie, ale nie bardzo miałem pomysł jak to zwizualizować. Później dopiero doszedłem do wniosku, że córka miałaby wzorzec nie taki sam, ale już zmodyfikowany, gdzie hasło byłoby już publiczne. tak czy inaczej przykład kiepskawy, ale w nocy różnie bywa z pomysłowością biggrin.gif

PS2: Może wydzielimy nasze posty począwszy od mojego zaczynającego się zdaniem "Widocznie za długo w C++ siedziałem." jako osobny topic? Tak chyba będzie lepiej. Tylko zaproponujmy nazwę. Może: "[php]Dziedziczenie a widoczność"?
Crozin
@thek:
Cytat
2. Nie opisuj na trzy akapity tego co jasno wynika (co da się łatwo zaobserwować samemu) z załączonych wcześniej listingów kodu (Twojego czy kogoś innego). Jedynie zniechęcasz do czytania wątków taką bezwartościową papką, którą niestety wypada przeczytać, bo może jednak coś sensownego jest tam napisane.


Rzeczywiście zachowanie PHP jest tutaj również i dla mnie dziwne i co najmniej zaskakujące - spodziewałem się jednak takiego zachowania. Wie ktoś może czym spowodowana jest taka implementacja w PHP?

Ad. PS2: To dobry pomysł.
thek
Ok... Temat wydzielony.

dla mnie także powinno być 'valueB' co zresztą zasugerowałem w poście, a dodatkowo Twoja implementacja w Javie także wykazała. Może problem leży w którejś z wersji PHP. Przetestuj może u siebie jeśli masz inną wersję. Jestem także ciekaw czy może Peter nam w rozkminianiu tego pomoże, bo na początku wskazał mi gdzie się walnąłem w rozumowaniu, więc może i teraz będzie miał jakiś pomysł "Czemu tak a nie inaczej?, bo dla mnie to nie jest błąd związany z enkapsulacją/hermetyzacją, ale to gdzieś głębiej siedzi. Na poziomie samej implementacji widoczności w języku. O ile nie uważam tego za poważny błąd, dzięki któremu może dojść do naruszenia bezpieczeństwa, to może to się przyczyniać do kompletnego zafałszowywania wyników. Choć kto wie.. Może ktoś inteligentniejszy lub sprytny użyje tego do wyciągania danych prywatnych klasy. Ja na razie tego nie potrafię sobie wyobrazić.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.