Tworząc w klasie element prywatny zakłada się, że będzie on dostępny tylko dla konkretnego obiektu i odwołanie do niego z zewnątrz nie będzie w ogóle możliwe. Aby umożliwić odczyt niektórych parametrów, stosowane są metody dostępowe:
<?php class something { private $element; public function getElement() { return $this -> element; } // end getElement(); } $object = new something; $object -> getElement(); ?>
Takie założenie sprawdza się w większości przypadków, jednak zastanówmy się, co się stanie, kiedy będziemy mieli dwa obiekty tej samej klasy i zechcemy, aby jeden z nich wykonał na drugim pewną operację. Pierwsze skojarzenie to użycie metod dostępowych:
<?php class something { private $element; public function getElement() { return $this -> element; } // end getElement(); public function cooperate(something $another) { return $another -> getElement(); } // end cooperate(); } $obj1 = new something; $obj2 = new something; $obj1 -> cooperate($obj2); ?>
Nie jest to jednak jedyny sposób. Ograniczenia widzialności dotyczą całych klas, nie zaś pojedynczych obiektów. Oznacza to, że każdy obiekt może bez trudu dostać się do pól i metod prywatnych innych obiektów tej samej klasy, czyli stosowanie metod dostępowych w takim przypadku nie jest konieczne. Możemy po prostu napisać:
public function cooperate(something $another) { return $another -> element; } // end cooperate();
Nie jest to żadne naruszenie bezpieczeństwa. Zauważmy, że jako twórcy klasy, mamy dokładne rozeznanie w jej strukturze, dlatego nie ma nic groźnego w tym, że dwa jej obiekty mają pełen dostęp do siebie nawzajem. W dalszym ciągu to my mamy kontrolę nad całym tym procesem, a nie programista, który z tej klasy będzie korzystać. Analogiczna zależność istnieje dla elementów chronionych. Powiedziałbym więcej - dzięki takiemu podejściu możemy lepiej hermetyzować nasz kod.
Znalazłem jak dotąd dwa przydatne zastosowania tej techniki. Przypadek pierwszy to jakieś drzewo. Klasy nie reprezentują tutaj poszczególnych elementów składowych aplikacji, od których wyprowadza się tylko jeden obiekt. Wręcz przeciwnie - jest klasa node i możemy mieć np. 100 jej obiektów. W każdym węźle mamy tablicę wszystkich dzieci. Chcąc udostępnić operację skopiowania wszystkich dzieci do innego węzła w tradycyjny sposób, musielibyśmy zrobić publiczną metodę dostępową zwracającą tę tablicę. Użytkownikowi naszego drzewa wcale nie musi być ona potrzebna, ponieważ może mieć np. strukturę zupełnie nieprzydatną nigdzie indziej poza implementacją. Niemniej, tworząc taką publiczną metodę, dalibyśmy mu możliwość odczytania jej. Lecz ponieważ dostęp do danych prywatnych rozciąga się na klasę, a nie na pojedyncze obiekty, kopiowanie może być zrobione prosto:
public function moveNodes(node $from) { $this -> nodeList = $from -> nodeList; $this -> nodeCount = $from -> nodeCount; } // end moveNodes();
Oczywiście jest to także korzystniejsze dla wydajności.
Wykorzystując właściwości słowa kluczowego protected, można zaimplementować funkcjonalność podobną do klas zaprzyjaźnionych z C++. Mamy trzy klasy, których obiekty muszą wymieniać się nawzajem pewnymi danymi, lecz końcowy użytkownik nie powinien mieć do nich dostępu. Są dwa wyjścia. Pierwsze to zdanie się na inteligencję użytkownika w sensie: jak odkryje te pola metodą reverse engineeringu i będzie się nimi bawić, zakładamy, że jest świadomy tego, co może się stać. Innymi słowy, czynimy te dane publicznymi. Drugie rozwiązanie znajduje się poniżej:
<?php class friendlyInfo { protected $field1; protected $field2; protected $field3; protected function method1(){} protected function method2(){} protected function method3(){} } class classA extends friendlyInfo { protected function method1() { // implementacja metody 1 } // end method1(); } class classB extends friendlyInfo { protected function method2() { // implementacja metody 2 } // end method2(); protected function method3() { // implementacja metody 3 } // end method3(); } class classC extends friendlyInfo { protected function sthElse(friendlyInfo $obj) { $obj -> method1(); $obj -> field1; } // end sthElse(); } ?>
Wszystko, co trzy klasy mają współdzielić, przenosimy do czwartej klasy bazowej, przy czym metody pozostawiamy puste tak, aby zainteresowane części kodu mogły wypełnić je właściwą implementacją. Niestety, taka pusta deklaracja pola czy metody siedzi w klasie nawet, jeżeli ta jej do niczego nie potrzebuje. Coś za coś.















Napisał phjr w piątek, 29 lutego 2008 o 11:43
Hmm, czy ta "emulacja" friendly methods to nie jest jednak nadużycie?
IMHO wygląda to dość nieczytelnie, i lepiej byłoby po prostu zrobić "ok, nie używamy na zewnątrz metod zaczynających się od znaku podkreślenia (_) i już". Pewną dyscyplinę i tak trzeba trzymać, więc to nie powinno sprawiać niesamowitych trudności.