Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Metody magiczne
Forum PHP.pl > Forum > PHP > Object-oriented programming
JoShiMa
dla mnie to chore wywoływac metodę magiczną wewnątrz klasy.
wookieb
Nieprawda. Jest sens, jeżeli niesie to za sobą jakieś dodatkowe operacje.
JoShiMa
Cytat(wookieb @ 25.02.2011, 10:14:05 ) *
Nieprawda. Jest sens, jeżeli niesie to za sobą jakieś dodatkowe operacje.

Nie. Nie ma sensu bo to samo mozna zrobić dowolna metodą choćby prywatną.
wookieb
Przykład
  1. class test {
  2. private $variable1 = 'test';
  3. private $variable2;
  4. public function setVariable1($value) {
  5. $this->variable1 = $value;
  6. $this->variable2 = null;
  7. }
  8.  
  9. public function setVariable2($value) {
  10. $this->variable2 = $value;
  11. $this->variable1 = null;
  12. }
  13. }

Widzisz sens? To teraz wyobraź sobie, że zastąpienie tych dwóch metod można zastąpić jedną, magiczną __set.
Jak najbardziej jest sens takiego działania.
Crozin
Sensowność metod magicznych sama w sobie jest sprawą mocno dyskusyjną. Ale nie widzę problemu by używać ich z wnętrza obiektu - sytuacja jest przecież dokładnie taka sama jak na zewnątrz.

EDIT:
Cytat
Jak najbardziej jest sens takiego działania.
No właśnie nie ma. smile.gif Bo kod staje się nielogiczny (nagle prymitywna operacja przypisania ($abc->def = 'xyz') zaczyna wykonywać jakieś poboczne operacje. A przecież można by utworzyć zwykłą metodę, która by jasno określała co robi. Co za różnica czy $abc->dev = .. czy $abc->set('dev', ...). Poza tym, że pierwszy zapis jest strasznie kłopotliwy.
wookieb
Cytat(Crozin @ 25.02.2011, 10:20:52 ) *
Sensowność metod magicznych sama w sobie jest sprawą mocno dyskusyjną.

Słyszałem o tym ale nadal nie widzę powodu by widzieć w tym jakiekolwiek przeciwwskazania.
JoShiMa
Cytat(wookieb @ 25.02.2011, 10:18:58 ) *
Przykład
  1. class test {
  2. private $variable1 = 'test';
  3. private $variable2;
  4. public function setVariable1($value) {
  5. $this->variable1 = $value;
  6. $this->variable2 = null;
  7. }
  8.  
  9. public function setVariable2($value) {
  10. $this->variable2 = $value;
  11. $this->variable1 = null;
  12. }
  13. }

Widzisz sens? To teraz wyobraź sobie, że zastąpienie tych dwóch metod można zastąpić jedną, magiczną __set.
Jak najbardziej jest sens takiego działania.


Zaręczam Ci, że to samo co możesz umieścić w finkcji __set() możesz umieścić w dowolnej metodzie. Skoro możesz ten kod zwinąć do metody __set() możesz go również zwinąć i umieścić w dowolnej metodzie. funkcje magiczne nie są aż tak magiczne jak się niektórym wydaje.

Daiquiri
Nie widzę przeszkód w stosowaniu metod magicznych, jeżeli ktoś ma ochotę - zwłaszcza że widzę ich zasadność. Będzie jednak (z tego co wiem) problem z Reflection API?
wookieb
Chyba jednak nie zdajecie sobie sprawy co mówicie. Metody __set i __get umożliwiają zapewnienie dodatkowej logiki w aplikacji tak samo jak "set*" i "get*". Co jeżeli mam 100 właściwości w klasie? 200 metod get i set? Strasznie słabe. Wolę sobie zrobić metodę magiczną __call w połączeniu __set i __get i mieć identyczna funkcjonalność w znaczniej bardziej efektywny sposób.

Mówiąc, że takie operacje można zastąpić dowolną inna metodą jest trochę utrudnianiem losu samemu sobie.
Podam doskonały przykład. W C++ są dostępne wektory. Dzięki przeciążeniu operatorów (coś ala "metoda magiczna") można na nich w bardzo łatwy sposób wykonywać operacje matematycze. Będziemy rezygnować z tak niewyobrażalnej wygody tylko po to, żeby "jasno" było widać w kodzie "set"? Straszne słabe.

Dla jasności przykładu.
Przykładowy wektor to (1,2). Dodanie do niego drugiego wektora (2,3) następuje w ten sposób. (1 + 2, 2 + 3) = (3, 5)

Sam stosuje w większości set i get ale to tylko daltego by zapewnić sobie wywołania łańcuchowe. Przy dużej ilości właściwości to nie ma żadnego sensu.
Crozin
Cytat
Słyszałem o tym ale nadal nie widzę powodu by widzieć w tym jakiekolwiek przeciwwskazania.
Co przemawia za metodami magicznymi?
- W pewnych przypadkach pozwalają uratować się przed utratą wstecznej kompatybilności.

Dodatkowe "zalety":
- krótszy kod - bo to właśnie różnica 4 znaków na linię świadczy o jakości kodu.

Wady:
- Kod staje się nielogiczny. Nagle operacje, które nie powinny mieć miejsca stają się możliwe i są kompletnie nieprzewidywalne.
- Brak wsparcia ze strony IDE.
- Niezbyt ciekawa sytuacja przy debuggowaniu/próbie zrozumienia kodu.
- Problemy przy dokumentowaniu kodu.
- A przede wszystkim... kod staje się magiczny czyli tracimy jego klarowność i jednoznaczność/oczywistość.

Cytat
Będzie jednak (z tego co wiem) problem z Reflection API?
Całe szczęście z poziomu refleksji nie istnieje coś takiego. Metody magiczne same w sobie niczym nie różnią się od tych normalnych. Jedynie interpreter przed wyrzuceniem błędu o braku metody/właściwości sprawdza czy aby przypadkiem jakaś magiczna metoda, która mogła by się tym zająć nie istnieje. Możesz odwoływać się do nich jak do każdej innej metody: $obj->__call('abc', array('def'));

EDIT:
Cytat
Co jeżeli mam 100 właściwości w klasie? 200 metod get i set? Strasznie słabe.
Strasznie słabe to jest posiadanie takiej ilości właściwości. Poza tym przecież mamy coś takiego jak... tablice i kolekcje.
  1. public function set($key, $value) {
  2. $this->myArray[$key] = $value;
  3. }
__set spisze się jedynie w przypadku, gdy dla wszystkich przypadków użycia będziemy mieli względnie taki sam algorytm. Bo jeżeli każdą zmienną miałbyś traktować indywidualnie skończyłbyś z dwoma setkami IFów.

Co do przeciążania operatorów. Faktycznie w przypadku wielu struktur matematycznych są fajne. Fajnie jest zrobić sobie $myVector - 5 czy $myMatrix * $myMatrix. Ale jedyne co zyskujemy to różnicę kilku znaków ($myVector.substract(5) VS $myVector - 5) a w zamian otrzymujemy mechanizm przez którego powstają potworki których zasada działania jest kompletnie nielogiczna/niezrozumiała/magiczna.
To samo spotkało metody magiczne w PHP - są one używane w nieprawidłowy sposób. Chyba tylko po to by ich używać.
wookieb
Cytat(Crozin @ 25.02.2011, 10:35:58 ) *
Wady:
- Kod staje się nielogiczny. Nagle operacje, które nie powinny mieć miejsca stają się możliwe i są kompletnie nieprzewidywalne.

No nie. Wszystko ma swoje zadania. I ktoś kto projektuje klasę wie o co chodzi. Potem odpowiednio udokumentować i po problemie.
Podałem Ci przykład wyżej. Jeżeli zmieniasz jedną wartość to druga się usuwa. Po co użytkownik po swojej stronie miałby to sam sprawdzać? A co gorsza kontrolować w takim przypadku kolejność wywołania "zmiany" właściwości? Jest to jak najbardziej wygodne. Poza tym "set" również może w takim wypadku spowodować Twoim zdaniem "nielogiczne" zachowania.

Cytat
- Brak wsparcia ze strony IDE.

Netbeans
Kod
@property-read
@property-write
@property
@method

I jechana.

Cytat
- Niezbyt ciekawa sytuacja przy debuggowaniu/próbie zrozumienia kodu.

To faktycznie jest troszkę utrudnione
Cytat
- Problemy przy dokumentowaniu kodu.

Czytaj wyżej. To, że PHPDOC chyba takich rzeczy nie wspiera to jego problem.

Cytat
- A przede wszystkim... kod staje się magiczny czyli tracimy jego klarowność i jednoznaczność/oczywistość.

Argument zbity wyżej.

P.S. Wydzielam.
Crozin
Przede wszystkim powinieneś myśleć nie o autorze klasy, a o jej użytkowniku (nawet jeżeli jest to sam autor).
  1. echo $obj->varA; // varA
  2. echo $obj->varB; // varB
  3.  
  4. $obj->varA = 'test';
  5. echo $obj->varB; // NULL - wtf?
Przecież nigdzie nie zmieniłem żadnej wartości, jedynie zmieniłem wartość właściwości varA. Usuwam przypisanie wartości - zwraca mi ładnie "varB", przywracam przypisanie wartości jakiejś innej właściwości - zwraca mi NULLa.
W taki oto prosty sposób kod staje się magiczny. O ile w przypadku gdy sam jesteś autorem i użytkownikiem dasz sobie bez problemu radę. Ale wyobraź sobie zewnętrzną bibliotekę z piętnastoma takimi obiektami, każdy z jakimś "zwalniającym nas z potrzebny niepotrzebnego pisania" potworkiem pod postacią __set(). A później dochodzą Ci kolejne 4 biblioteki z kolejnymi potworkami - idzie się pochlastać w końcu.

Przykład drugi:
  1. echo $obj->getVarA(); // varA
  2. echo $obj->getVarB(); // varB
  3.  
  4. $obj->setVarA('test');
  5. echo $obj->getVarB(); // NULL - wtf?
Ale reakcja jest trochę inna - od razu wiadomo, że jedynym możliwym miejscem zmiany wartości varB jest metoda setVarA().

Trzeba jeszcze zaznaczyć, że nie jest to najlepszy kod. 10 getterów/setterów najparwdopodobniej powinno być zastąpionych jedną parą, a operacje powinny być wykonywane na tablicy.

Trzeba pamiętać, że cała magiczność ogranicza się do zamiany $obj->__set('var', $value) na $obj->var = $value. Naprawdę nie zyskujemy niczego poza kilkoma znaczkami.

Ahh.. mogłaby to być jeszcze metoda getVarB() (która przykładowo zwracałaby raz varB a raz NULL w zależności od ilości wywołań) - ale wtedy do pierwszego listingu należałoby też dodać wtf spowodowane przez __get() wink.gif
wookieb
To może podam przykład z życia.
  1. class Resource
  2. {
  3.  
  4. /**
  5.  * Cache aplikacji
  6.  * @var Cache
  7.  */
  8. private $cache
  9.  
  10. /**
  11.  * Flaga czy używać cache
  12.  * @var boolean
  13.  */
  14. private $useCache = false;
  15.  
  16. public function __set($name, $value) {
  17. if ($name == 'cache' && $value instanceof Cache) {
  18. $this->cache = $value;
  19. $this->useCache = true; // sporna linijka
  20. } else $this->useCache = (bool)$value;
  21. }
  22.  
  23. public function __get($name) {
  24. if (isset($this->$name)) return $this->$name;
  25. }
  26. }

Pomińmy piękno tego kodu.
Dlaczego linijka oznaczona komentarzem nie miałaby istnieć?
Przecież z logicznego punktu widzenia skoro podajemy obiekt Cache do klasy to nie po to, żeby sobie był, tylko żeby z niego skorzystać? Nielogicznym byłoby w tym przypadku wymuszenie konieczności ustawienia flagi używana Cache.
A co gdybym dopisał opcję ustawiania flagi $useCache na false w przypadku usunięcia obiektu Cache?
To jest jak najbardziej logiczne zachowanie.
Klasa to nie tablica, której przypisujemy po prostu wartości do właściwości. Służy do wykonywania konkretnych zadań w których mieści się kontrola nad wartościami w jej wnętrzu.
Drugi przykład.
  1. class Session {
  2. /**
  3.  * uzytkownik przypisany do sesji
  4.  * @var User
  5.  */
  6. private $user;
  7.  
  8. private $id;
  9. public function __set($name, $value) {
  10. switch($name) {
  11. case 'id':
  12. $this->id = $value;
  13. if (!$value) $this->user = null; // usuwamy obiekt użytkownika skoro usunięto id sesji
  14. }
  15. }
  16. }

Dlaczego nie miałbym usunąć obiektu użytkownika skoro nie ma id sesji co oznacza, że nie powinno jej być.

Oczywiście identyczne zachowania można zrobić za pomocą get i set, tylko co stoi na przeszkodzi aby wykorzystać w tym celu magię? Dla mnie jest to tylko różnica "przyzwyczajeń", niczego więcej.
thek
To ja dorzucę jeszcze moją uwagę. Funkcje magiczne, jako wewnętrzne klasy mają dostęp do wszystkiego i są widoczne/ tworzą funkcje jako public z zewnątrz. W efekcie Wywołanie nieistniejącego settera/gettera, daje nam dostęp do wszystkich atrybutów, niezależnie od ich specyfikatora dostępu (!). W tym momencie poszło się paść private i protected. Wszystko jest widoczne dla wszystkich. Stworzenie więc jednego do wszystkiego to zakładanie sobie sznura na szyję.
wookieb
Cytat(thek @ 25.02.2011, 12:34:21 ) *
To ja dorzucę jeszcze moją uwagę. Funkcje magiczne, jako wewnętrzne klasy mają dostęp do wszystkiego i są widoczne/ tworzą funkcje jako public z zewnątrz. W efekcie Wywołanie nieistniejącego settera/gettera, daje nam dostęp do wszystkich atrybutów, niezależnie od ich specyfikatora dostępu (!).

Zastanów się z 10 razy i napisz post jeszcze raz. Poza tym __get i __set nie służy jedynie do uzyskiwania dostępu do zmiennych prywatnych i chronionych.
cojack
Dla mnie jest rzeczą okrutną że nie można __set i __get ustawić na private ;(
Eby
Z mojej perspektywy to trochę tak jakby sprzeczać czy lepiej użyć "return" czy "echo".
Jednego i drugiego należy używać tam gdzie to jest potrzebne (ułatwia życie, tworzy kod łatwiejszy do modyfikacji itp).
Np. używając metod magicznych do view helperów (niezbyt ładny przykład) :
  1. echo helper->tag('a', 'more', array('href'=>'controller::action','class'=>'link'));

oczywiście zamiast tego (bo to magiczne, nieładne i nieczytelne) można użyć:
  1. $taghelper = new View_Helper_Tag();
  2. $taghelper->setTag('a');
  3. $taghelper->setContent('more');
  4. $taghelper->setParam('href', array('controller'=>'controller','action'=>'action'));
  5. $taghelper->setParam('class', 'link');
  6. echo $taghelper->returnHtml();

Moim zdaniem pierwszego kodu używa się łatwiej i szybciej niż drugiego - zwłaszcza w sytuacji gdy mamy 700 linii w szablonie i nie chcemy z jakichś powodów tworzyć ich tam 4000.
Tego samego helpera i metod magicznych można użyć w klasie która służy do budowania całego grida czy formularza lub w samej klasie helper by nie dublować kodu.
Teraz gdy dochodzi dodatkowy rodzaj helpera, wystarczy dodać odpowiednią klasę obsługi a samego wrapera już nie trzeba ruszać.
Oczywiście możemy zrobić fabrykę która zwraca odpowiedni rodzaj helpera i wtedy nie mamy części kłopotów, ale pojawiają się inne - każde podejście ma swoje wady i zalety i składni języka należy użyć w zależności od potrzeb (i naszych preferencji).
thek
Cytat(wookieb @ 25.02.2011, 12:37:50 ) *
Zastanów się z 10 razy i napisz post jeszcze raz. Poza tym __get i __set nie służy jedynie do uzyskiwania dostępu do zmiennych prywatnych i chronionych.
Wookieb... Dobrze napisałem. Używając __set i __get, tworzymy metodę, która udostępnia wszystkim na zewnątrz atrybuty klasy. Jeśli byśmy tego nie chcieli w pełni, to musimy to ograniczać już wewnątrz definicji tej metody. Przykładowo mamy wspomnianą przez Ciebie klasę z 200 atrybutami, w tym 5 private i 5 protected, z których nie wszystkie chcemy udostępniać na zewnątrz. Co robimy? Sięgamy po refleksję by sprawdzić, czy dany atrybut jest na "black-liście"? Fajnie... Kosztem naszej wygody (linie kodu) zarżynamy wydajnościowo aplikację, która oprócz magii jeszcze po refleksję sięga.

O to mi chodziło w przykładzie. __set i __get oraz inna magia daje dostęp nie zawsze w sposób jaki byśmy chcieli ostatecznie uzyskać i musimy nad tym panować. Poza tym powiedz mi jedno wookieb: "Czy __set i __get ruszają do działania jeśli atrybut jest public?", bo jak dla mnie nie. Sam kwantyfikator zezwala na grzebanie mi w zmiennych public, więc o czym my mówimy tutaj? O pisaniu settera i gettera magicznego dla czegoś, co i tak nie będzie użyte? Bo klasa mając składową publiczną odda nam ją od razu i odwołanie się do magii nie nastąpi nigdy?


Dla mnie większy sens mają te funkcje tylko gdy chcemy się zająć wyłapywaniem błędów własnych, a nie posługiwanie tym w sposób nadmierny. To tak jak z mechanizmem wyjątków. Powstał by prościej obsługiwać sytuacje wyjątkowe, a ludzie używają tego do kontroli przepływu danych w aplikacji niczym wariant IF, który dodatkowo trudniej wysypać bo jest "inteligentniejszy".

PS... Tak wiem... Pozwala utworzyć i odczytywać w locie obiektowi nieistniejące składowe choćby, ale IMHO takie posługiwanie się wynika ze zrypanego procesu projektowego. Źle zaprojektowana aplikacja po to sięgnie. Dobrze przemyślana nie potrzebuje takich łat.
JoShiMa
Cytat(wookieb @ 25.02.2011, 10:33:46 ) *
Chyba jednak nie zdajecie sobie sprawy co mówicie. Metody __set i __get umożliwiają zapewnienie dodatkowej logiki w aplikacji tak samo jak "set*" i "get*". Co jeżeli mam 100 właściwości w klasie? 200 metod get i set? Strasznie słabe. Wolę sobie zrobić metodę magiczną __call w połączeniu __set i __get i mieć identyczna funkcjonalność w znaczniej bardziej efektywny sposób.

Ale co Ci uniemożliwia wstawienie kodu, który umieściłbyś w __set() wstawić do jednej metody set i nie mieć ich 100?

  1. function set($varName,$value){
  2. $this->$varName = $value;
  3. }


Zadziała nawet jeśli nie istnieje taki parametr klasy, bo stworzy go w locie.

I po co Ci funkcja magiczna?


Nie twierdzę, że funkcje magiczne są zupełnie do bani. Jednak używanie ich wewnątrz klasy to totalny bezsens.
Crozin
@Eby: Jak można porównywać return do echo? Poza tym ten przykład to też wiele z tematem wspólnego nie ma. Jak już to powinieneś porównać takie zapisy:
  1. echo $helper->tag('a', 'more', array('href'=>'controller::action','class'=>'link'));
  2. echo $helper->helper('tag', 'a', 'more', array('href'=>'controller::action','class'=>'link'));
  3. echo $helper->helper('tag', array('a', 'more', array('href'=>'controller::action','class'=>'link')));
Chociaż osobiście wszystkie uważam za niezbyt trafne.

@wookieb: Co do pierwszego listingu... jest on chyba niekompletny bo kompletnie nie trzyma się kupy - albo ja nie potrafię sobie wyobrazić co ten kod miałby robić (konkretniej metoda __set w przypadku gdy podajemy klucz inny niż "cache"). Poza tym już uwidacznia się tu pierwszy "wtf". Na metodę __set() spada obowiązek wykonywania wielu zadań - czy nie lepiej by było:
  1. function setCache(CacheInterface $cache) {
  2. $this->cache = $cache;
  3. $this->useCache = true;
  4. }
  5.  
  6. public function set($key, $value) {
  7. //...
  8. }
I używać kodu normalnie, tj:
  1. $r = new Resource();
  2. $r->setCache($myCacheImpl);
  3. $r->set('xyz', 'aaa');
  4.  
  5. // zamiast
  6. $r->cache = $myCacheImpl;
  7. $r->xyz = 'aaa';
Raczej nie przekonasz mnie, że interfejs drugiego rozwiązania jest lepszy... bo jak dla mnie to jest on okropny i podatny na błędy.

Cytat
A co gdybym dopisał opcję ustawiania flagi $useCache na false w przypadku usunięcia obiektu Cache?
Dodać metodę odpowiedzialną za usuwanie cache, która po usunięciu zmieni flagę?

Cytat
Klasa to nie tablica, której przypisujemy po prostu wartości do właściwości. Służy do wykonywania konkretnych zadań w których mieści się kontrola nad wartościami w jej wnętrzu.
Gdy pisałem o tablicach/kolekcjach miałem na myśli taką sytuację:
  1. class AbcMyClass {
  2. protected $storage = array();
  3.  
  4. public function get($key, $default = null) {
  5. return array_key_exists($key, $this->storage) ? $this->storage[$key] : $default;
  6. }
  7.  
  8. public function set($key, $value) {
  9. $this->storage[$key] = $value;
  10. }
  11. }
  12.  
  13. class AbcMyClass {
  14. public function __get($key) {
  15. return isset($this->{$key}) ? $this->{$key} : null;
  16. }
  17.  
  18. public function set($key, $value) {
  19. $this->$key = $value;
  20. }
  21. }
Pomijając fakt o ile bardziej podatne na błędy i mniej elastyczne jest drugie rozwiązanie (ograniczone możliwości dla nazwania kluczy, problem gdy nazwa klucza jest w zmiennej) spójrzmy na całą różnicę pomiędzy użyciem obu rozwiązań:
  1. $obj->get('parameterA');
  2. $obj->parameterA;
  3.  
  4. $obj->set('parameterA', 123);
  5. $obj->parameterA = 123;
  6.  
  7. $obj->get($abc->getKey());
  8. $key = $abc->getKey(); $obj->$key; // nie jestem pewien czy $obj->{$abc->getKey()}; zadziała. Zrezstą za używanie zmiennych zmiennych powinno się coś obcinać


Co do drugiego przypadku (z klasą Session). Znowu ten sam problem - dziesiątki IFów. Na pewno żaden z nas nie byłby szczęśliwy mając wykorzystywać tą klasę. Takie rozwiązanie dla mnie byłoby do zaakceptowania wyłącznie w przypadku konieczności zachowania wstecznej kompatybilności. Tj. zakładając, że mamy już kod, klasy, która ma publiczną właściwość "id" musimy dodać jeszcze opcję usuwania użytkownika gdy jest ona fałszem. Paskudne rozwiązanie ale jedno z niewielu jakie pozwala na zachowanie tej nieszczęsnej kompatybilności.
cojack
Nudni jesteście, ilu programistów tyle pomysłów jak wykorzystać metody magiczne. Zamknijcie ten temat.
Eby
Cytat(Crozin @ 25.02.2011, 13:51:03 ) *
@Eby: Jak można porównywać return do echo? Poza tym ten przykład to też wiele z tematem wspólnego nie ma. Jak już to powinieneś porównać takie zapisy:
  1. echo $helper->tag('a', 'more', array('href'=>'controller::action','class'=>'link'));
  2. echo $helper->helper('tag', 'a', 'more', array('href'=>'controller::action','class'=>'link'));
  3. echo $helper->helper('tag', array('a', 'more', array('href'=>'controller::action','class'=>'link')));
Chociaż osobiście wszystkie uważam za niezbyt trafne.

Wybacz, nie dokładnie opisałem o co mi chodzi przedstawiając jedynie pewien efekt końcowy.
Pisząc o wrapperze "helper" w temacie o metodach magicznych miałem na myśli raczej coś takiego :
  1. <?php
  2. class helper {
  3. protected $_obj = array();
  4. ...
  5. public function __call($name, $arg) {
  6. if ( !isset($this->_obj[ $name]) ) {
  7. $filename = 'View_Helper_' . $name;
  8. require_once( $filename );
  9. $this->_obj[ $name ] = new $filename();
  10. }
  11. return $this->_obj[ $name ];
  12. }
  13. }
  14.  
  15. class View_Helper_Tag {
  16. ...
  17. public function __toString() {
  18. return $this->render();
  19. }
  20. public function render() {
  21. $str = "<".$tag;
  22. ...
  23. if ( in_array($tag, $this->_autoclose) )
  24. $str .= '/>';
  25. else
  26. $str .= '>';
  27. ...
  28. return $str;
  29. }
  30. }

Owszem Twój zapis zrobi to samo, ale ja na końcu mam pewien obiekt z którym mogę coś jeszcze zrobić (idziemy w kierunku aspx i kontrolek) - i wyrzucenie tego na standardowe wyjście jest tylko jedna z możliwości. Twój zapis tego nie umożliwia niestety.
Zyx
wookieb -> i ja bym ten Twój przykład z życia zapisał tak:

  1. class Resource
  2. {
  3. public function set($name, $value)
  4. {
  5. // ...
  6. }
  7. public function get($name)
  8. {
  9. // ...
  10. }
  11. } // end Resource;


Wszystko, co da się zrobić za pomocą metod magicznych, da się równie elegancko zrobić zwykłymi wywołaniami metod. I taki kod będzie w dodatku dużo czytelniejszy. Ostatnio musiałem wrócić do Symfony 1.x i Doctrine 1.x i tracę tam naprawdę sporo czasu na radzenie sobie z różnymi dziwnymi i niepożądanymi dla mnie skutkami tego, że połowa tego, co się dzieje w tym frameworku to czysta magia. Pisze sobie człowiek metodę i nagle mu się rozpierdziela odwołanie do... pola, albo jeszcze śmieszniej: odwołanie tablicowe w zupełnie innej części kodu. Nie będę wspomniał już o wynalazkach w stylu tych, co zaproponował Eby, które też dokładają swoje idiotyzmy.

Kod
$helperCollection->getHelper('tag')->render();


Ponadto thek ma rację. Jeśli pole jest zadeklarowane np. jako chronione, a my próbujemy dostać się do niego spoza klasy, wywoła się metoda magiczna. Jeśli ją źle napiszemy, będziemy mogli się do niej dostać. A tak w ogóle to podział private, protected i public ma sens tylko jako zachęta do stosowania pól zgodnie z przeznaczeniem. Jak komuś będzie zależeć, zawsze da się w ten czy inny sposób wartość pola prywatnego odczytać spoza obiektu bez względu na to, jak się zabezpieczymy. Najprostsze wyjście (od PHP 5.3) to refleksja.
Eby
Cytat(Zyx @ 25.02.2011, 14:12:11 ) *
Nie będę wspomniał już o wynalazkach w stylu tych, co zaproponował Eby, które też dokładają swoje idiotyzmy.
Kod
$helperCollection->getHelper('tag')->render();

I tutaj niestety Twoje rozwiązanie jest moim zdaniem mniej czytelne (jeśli jeszcze dodasz do tego dodatkowe parametry które były w poprzednim poście) - wiec (znów moim zdaniem) nie zyskałeś nic - ani czytelności kodu ani w zasadzie szybkości wykonywania... Mniejszą możliwość popełnienia błędu - co jest tylko teorią, gdyż patrząc na kod w jednym i drugim przypadku jesteś w stanie stwierdzić jak działa. Oczywiście twój zapis jest dłuższy jedynie o kilkanaście znaków co sprowadza się do wydłużenia kodu szablonu o 25% nie zyskując w praktyce nic.
Zyx
Jakie "dodatkowe parametry", kiedy nic z tymi parametrami w __call() nie robisz?

1. Czytelność kodu - doprawdy? To spróbuj sobie znaleźć w dokumentacji później opis do takiego wywołania.

2. Szybkość wykonania - OMG, a skąd Ci się coś takiego wzięło? Kto tu o jakiejś szybkości wykonania mówi? Zresztą, patrząc jakie ludzie wymyślne parsery nazw funkcji i pól potrafią wsadzić do metod magicznych, mam wątpliwości co do wydajności. Co więcej, jeśli mamy mnóstwo metod magicznych, w zasadzie żeby cokolwiek zoptymalizować, najlepiej wszystko wczytać do własnych zmiennych i prostych tablic. Jak można mówić o optymalizacji, kiedy nie masz pojęcia o tym, z czym masz do czynienia? Piszesz: $helper->tag. To dobrze czy źle? Zapisywać do zmiennej tymczasowej czy nie zapisywać? Szybko to działa czy wolno? Co się wtedy dzieje? Nawet jest problem ze sprawdzeniem tego, bo: patrz punkt 1.

3. Długość kodu... no cóż, jak się nie wpadnie na to, że wynik długiego i skomplikowanego wywołania można sobie zapisać do zmiennej i używać wielokrotnie...
wookieb
Cytat(thek @ 25.02.2011, 13:39:36 ) *
Wookieb... Dobrze napisałem. Używając __set i __get, tworzymy metodę, która udostępnia wszystkim na zewnątrz atrybuty klasy. Jeśli byśmy tego nie chcieli w pełni, to musimy to ograniczać już wewnątrz definicji tej metody. Przykładowo mamy wspomnianą przez Ciebie klasę z 200 atrybutami, w tym 5 private i 5 protected, z których nie wszystkie chcemy udostępniać na zewnątrz. Co robimy? Sięgamy po refleksję by sprawdzić, czy dany atrybut jest na "black-liście"? Fajnie... Kosztem naszej wygody (linie kodu) zarżynamy wydajnościowo aplikację, która oprócz magii jeszcze po refleksję sięga.

Thek błagam Cie. Myśl czasem. To, że __get i __set działają przy PRÓBIE dostania się do właściwości prywatny WCALE nie oznacza, że chcemy ich do tego używać. To, że widziałeśtylko takie zastosowanie tej metody wcale nie znaczy, że jest jedyne. Opcji jest całe mnóstwo.


Cytat
O to mi chodziło w przykładzie. __set i __get oraz inna magia daje dostęp nie zawsze w sposób jaki byśmy chcieli ostatecznie uzyskać i musimy nad tym panować. Poza tym powiedz mi jedno wookieb: "Czy __set i __get ruszają do działania jeśli atrybut jest public?", bo jak dla mnie nie. Sam kwantyfikator zezwala na grzebanie mi w zmiennych public, więc o czym my mówimy tutaj? O pisaniu settera i gettera magicznego dla czegoś, co i tak nie będzie użyte? Bo klasa mając składową publiczną odda nam ją od razu i odwołanie się do magii nie nastąpi nigdy?

Ale co to ma do rzeczy? Wszystko zależy od projektu klasy. Jeżeli ktoś miesza magię z publicznymi właściwościami to jego problem. To tak jakby mówić, że żelazko jest BE bo można się nim poparzyć... Wszystko z rozwagą. Proszę Cię THEK, wiem że lubisz się wygadać ale ty już nie rozmawiasz. LEJESZ WODĘ mówiąc o rzeczach oczywistych.

Cytat
PS... Tak wiem... Pozwala utworzyć i odczytywać w locie obiektowi nieistniejące składowe choćby, ale IMHO takie posługiwanie się wynika ze zrypanego procesu projektowego. Źle zaprojektowana aplikacja po to sięgnie. Dobrze przemyślana nie potrzebuje takich łat.

A widział ty kiedyś coś takiego jak settery i gettery w innych językasz? Wiem, że chyba w JS gdzieś to dało się zrobić, w AS na pewno. Co to dawało? Znacznie łatwiejsze w użyciu reagowanie na zmiany pewnych wartości. I sry ale nie przekonasz mnie, że jest to brzydkie. Jest piękne w swojej prostocie i wykorzystaniu.


Cytat
Wszystko, co da się zrobić za pomocą metod magicznych, da się równie elegancko zrobić zwykłymi wywołaniami metod. I taki kod będzie w dodatku dużo czytelniejszy. Ostatnio musiałem wrócić do Symfony 1.x i Doctrine 1.x i tracę tam naprawdę sporo czasu na radzenie sobie z różnymi dziwnymi i niepożądanymi dla mnie skutkami tego, że połowa tego, co się dzieje w tym frameworku to czysta magia. Pisze sobie człowiek metodę i nagle mu się rozpierdziela odwołanie do... pola, albo jeszcze śmieszniej: odwołanie tablicowe w zupełnie innej części kodu. Nie będę wspomniał już o wynalazkach w stylu tych, co zaproponował Eby, które też dokładają swoje idiotyzmy.

Powiedz to fanom CAKEPHP.

Chwila stopu. O co my się kłócimy?
Co jest ładniejsze?
Cytat
$klasa->set('pole', 'wartosc');

// czy
$klasa->pole = 'wartosc';

Przecież to zależy od preferencji i swoje upodobania.
1 przyda się do wywoływania łańcuchowego a drugie do pracowania na obiekcie klasy jak na STDClass. I tak 2 rozwiązania jest zarąbiście wygodne.

Ja mam cały czas wrażenie, że jesteście zamknięci w schemacie MAGIA = dostęp do włąściwości private i protected. Skończcie z tym bo zastosowań __get i __set jest dużo dużo więcej.
Crozin
Przecież napisałem, że w przypadku metod __get() i __set() rozchodzi się wyłącznie o sposób w jaki można wywołać te metody. Cały wątek właściwie rozchodzi się o konsekwencje udzielenia takiego zapisu i faktu, że metody te są wywoływane nawet wtedy gdy być nie powinny. Również nie rozumiem o co chodzi z tym dostępem do prywatnych właściwości.
thek
Wookieb. Ja nie jestem anty-magic zatwardziały. Nie jestem przeciwko magii, ale przeciwko nadużywaniu jej, zwłaszcza w sytuacjach gdzie nie musi być uzywana. Przykład jaki podałem miał na celu jedynie pokazanie jak magia bywa zwodnicza i dlaczego trzeba z nią uważać, gdyż nieumiejętne zabieranie się za nią może przynieść więcej szkody niż pożytku. To, że ułatwia dużo rzeczy, nie oznacza, że trzeba po nią sięgać gdzie popadnie. To tak jak z samochodem. To, że ma na liczniku 220, nie oznacza, że przy każdym wyjeździe trzeba go "zamknąć".
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.