Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Przydatnosc "getterow" i "setterow"
Forum PHP.pl > Forum > PHP > Object-oriented programming
Stron: 1, 2
marcini82
Witam!

Chcialbym poznac Wasza opinie na temat zwyczaju holdowanego przez czesc programistow, a mianowicie tworzenia metod typu
  1. <?php
  2. class testClass{
  3.  
  4. public $property;
  5.  
  6. getProperty(){
  7. return $this->property;
  8. }
  9.  
  10. setProperty($property){
  11. $this->property = $property; 
  12. }
  13. }
  14. ?>

Czy czesto to stosujecie i jakie macie z tego korzysci?
Oczywiscie to dosc eleganckie, zawsze mozna dopisac jakies warunki sprawdzajace poprawnosc danych itp.
Ale z drugiej strony pewnie rzadko te metody zostaja rozbudowane ponad schemat, ktory podalem wyzej (brak czasu i/lub checi), a czy bez dodatkowego kodu to ma sens? Napisanie tych funkcji to zmudna praca, mozna ja sobie czasem ulatwic za pomoca jakiegos generatora, ale tez nie dla kazdej wlasciwosci taka metoda jest sensowna. Poza tym, obecnosc wielu metod tego typu w klasie pogarsza troche czytelnosc jej kodu i odwraca uwage od metod naprawde istotnych.

Jesli mamy do czynienia z wazna klasa , ktora jest podstawa dla polowy aplikacji (albo bardzo uniwersalna, ktora bedziemy wielokrotnie uzywac w innych projektach), albo z jakimis kluczowymi wlasciwosciami, to mozna sie pokusic o metody ktore elegancko pobiora lub ustawia te wlasciowosci.
Ale czy zawsze jest sens sie w to bawic?

Ciekaw jestem Waszych opinii...
menic
U mnie ilośc własciwosći publicznych jest znikoma. Tak wiec problem "nieczytelnosci" kodu tym spowodowany odpada. Natomiast własciwosci prywatne zmieniam bez tch wszystkich setterow.
IMHO zbyt dużo zmiennych w klasie świadczy o złym zaprojektowaniu klasy winksmiley.jpg
Turgon
PHP5 dostarcza __get i __set i jak przetestował Bastion są szybsze od settów i gettów smile.gif Więc też ich używam ;] Z resztą menic: dużo zmiennych wcale nie świadczy o złym zaprojektowaniu, ale pewne klasy muszą je niestety mieć smile.gif
mysz
Settery tego typu są brzydkie jak dla mnie. Zdecydowanie wolę wymienione przez Turgona magiczne właściwości __set i __get. Są znacznie czytelniejsze. Przykład kodu, jaki lubię stosować:
  1. <?php
  2. class Test {
  3. private $_data = array();
  4. public function __set($key, $value) {
  5. if (method_exists($this, '__check_' . $key)) {
  6. if (call_user_func_array(array($this, '__check_'.$key), $value)) {
  7. $this->_data[$key] = $value;
  8. } else {
  9. throw new Exception('nieprawidłowa wartość');
  10. }
  11. } else {
  12. $this->_data[$key] = $value;
  13. }
  14. }
  15. public function __get($key) {
  16. if (isset($this->_data[$key])) {
  17. return $this->_data[$key];
  18. } else {
  19. throw new Exception('brak właściwości');
  20. }
  21. }
  22. public function __tostring() {
  23. return sprintf('%s: %s', get_class($this), print_r($this->_data, 1));
  24. }
  25. public function __check_c($data) {
  26. return is_numeric($data);
  27. }
  28. }
  29. ?>


Użycie:
  1. <?php
  2. $test = new Test();
  3. $test->a = 'a';
  4. echo $test->a;
  5. echo '<br />';
  6. $test->b = 'b';
  7. echo $test->b;
  8. echo '<br />';
  9. $test->c = '123';
  10. echo $test->c;
  11. echo '<br />';
  12. try {
  13. $test->c = 'ass';
  14. echo $test->c;
  15. } catch (Exception $m) {
  16. print 'Jakiś problem: ' . $m->getMessage();
  17. }
  18. echo '<br />';
  19. echo $test;
  20. ?>


Oczywiśćie to powyżej to tylko bardzo prosty przykład, który wymaga dopracowania i rozbudowania, ale pokazuje jakie fajne możliwości tkwią w magicznych właściwościach :) Oraz, że zwykłe settery, w postaci znanej z Javy (setVar, getVar) są do tyłka... ;)
envp
Hah no świetnie, OOP polega na pisaniu jak najbardziej rozszezalnego kodu wiec lepiej sobie zrobic setZmienna() i getZmienna(), bo za miesiac okaze sie ze do class :: $zmienna ludziska wrzucaja wam śmiecie i potrzeba zrobić walidowanie danych przed zapisaem do zmienniej. Wlasnie przy samym zapisywaniu w klasie. Przyklad wyuskany, ale jak najbardziej pasuje do tego posta.
splatch
Cytat(mysz @ 16.03.2007, 22:17:05 ) *
Oczywiście to powyżej to tylko bardzo prosty przykład, który wymaga dopracowania i rozbudowania, ale pokazuje jakie fajne możliwości tkwią w magicznych właściwościach smile.gif Oraz, że zwykłe settery, w postaci znanej z Javy (setVar, getVar) są do tyłka... winksmiley.jpg

Settery w Javie (jak i cała koncepcja JavaBeans) powstały na długo przed pojawieniem się PHP5 zatem krytykowanie Javy, za to, że nie ma metod "magicznych" mija się z celem.
Jest to przede wszystkim język statyczny, gdzie w chwili kompilacji powinna być znana już nazwa wykonywanej metody. Wywołania dynamiczne można zrealizować tylko przy pomocy Reflection API.
To nie jest PHP, gdzie możesz sobie bez jakichkolwiek konsekwencji skleić nazwę funkcji i zrobić $bar($foo) i $bar->$foo = $baz. Nie zapominaj o tym w swych ocenach. Na getterach możesz zbudować bardzo złożone mechanizmy - a klasy zaprojektowane w ten sposób bez problemu użyć do wyklikania interfejsu użytkownika w NetBeans (wskazane dodanie PropertyDescriptora). Nie musisz również bawić się z obserwatorami - wystarczy użyć PropertyChangeSupport i zaimplementować interfejs PropertyChangeListener w zależnym obiekcie.
W get/set z czasem coraz częściej pojawia się dodatkowy kod, od chociażby w celu poinformowania listenerów o zmianie w obiekcie ..
Pisząc w JSP możesz bez problemu korzystać z ${obj.property}, co w wyniku, po skompilowaniu do servletu da Ci obj.getProperty().

Jako ciekawostkę dodam, że w Javie 7 pojawią się prawdopodobnie bloki, w których będzie się deklarować property dostępne via get/set.
mysz
@splatch: pojęcie kontekstu wypowiedzi jest Ci obce? smile.gif mało mnie interesuje Java, napomknąłem o niej tylko w kontekście tego, że to właśnie ten język upowszechnił taki zapis setterów i getterów. PHP5 daje znacznie wygodniejsze możliwości, których prosty i nierozbudowany przykład podałem. Nie mówiąc o tym jak wygodnie jest to rozwiązane w Pythonie czy C#, ale to już drobny szczegół...
mike
Setery i getery pozwalają ba na bardzo szczegółową kontrolę dostępu do pół oraz metod obiektu. Są też najlepszym sposobem na walidację danych.

Dostęp za pomocą metod magicznych to głupota, nic nie daje a ogranicza w kilu rzeczach. Nie zrobisz kontroli dostępu walidacji i innych takich rzeczy.
A najgłupszą rzeczą jest to że takie rozwiązanie wymusza trzymanie wszystkich danych w tablicy. Chore, po to są pola, żeby każde trzymało coś innego a nie jedna wielka tablica, która jest rozwiązaniem wolnym i niewygodnym oraz nie możliwym d udokumentowania.
Turgon
mike_mech, a kto tak powiedział ?
  1. <?php
  2. class example{
  3. protected $var = '';
  4.  
  5. public function __set($name,$value){
  6. if(!isset($this->{$name})){
  7. throw new Exception('Wartość '.$name.' nie istnieje, więc nie może być edytowalna!');
  8. }
  9. $this->{$name} = $value;
  10. }
  11.  
  12. public function __get($name){
  13. if(!isset($this->{$name})){
  14. throw new Exception('Wartość '.$name.' nie istnieje, więc nie może być pobierana!');
  15. }
  16. return $this->{$name};
  17. }
  18. }
  19. try{
  20.  
  21. $o = new example();
  22.  
  23. $o->var = 'Jakieś tam bzdury!';
  24.  
  25. echo $o->var;
  26.  
  27. $o->inna = 'Dupa'; 
  28. }
  29. catch(Exception $e){
  30. echo $e->getMessage();
  31. }
  32. ?>


Otrzymuje to co chciałem. Banalną kontrolą dostępu jest nie dodawanie setterów do zmiennych nie publicznych, które mają być nie edytowalne, a zmienne edytowalne jako publiczne. Też kto Ci zabronił używania obiektów kolekcji? Wolna amerykanka... Za to uwielbiam PHP.

EDIT:
Poprawiłem przykład. I dałem jeszcze prostszą kontrolę choć wymaga pewnych nawyków =] Np. dawania na początek zmiennym edytowalnym pustej wartości =]
mysz
@mike_mech: a mówiłem że przykład jest prosty i potrzeba go rozbudować...
Turgon podał jeden z przykładów jak można zrobić rozdzielenie pól, żeby nie trzymać wszystiego w tablicy. A tablicę taż można w prosty sposób udkokumentować, wystarczy chcieć. Już lepiej by było gdybyś napisał że wg Ciebie fajniejsze jest używanie metod set() i get(). Chyba że napiszesz coś konkretnego w kontekście anty.

Co do walidacji: przecież w moim przykładzie było pokazana i zaimplementowana walidacja, patrzyłeś w ten kod? (hint: spróbuj w moim przykładzie podstawić do zmiennej 'c' wartość nie numeryczną).

Plusem korzystania z metod magicznych jest czytelność w używaniu kodu. Nie interesują Cię żadne metody, korzystasz z naturalnego przypisywania i pobierania wartości zmiennych. Nie znam przypadku w którym magiczne __set() i __get() nie zastąpiłyby z powodzeniem starych set() i get(). Za to właśnie pojawia się wspomniana już przeze mnie naturalność używania kodu.

Nie musisz tego lubić, nie musisz używać, ale proszę użyj jakiegoś argumentu który ma rzeczywiste podstawy, a nie takiego który można obalić w ciągu kiliku minut po jakimś zastanowieniu się.
Turgon
I apropos walidacji. Można to jeszcze prościej. Klasa dziedziczy po Validatorze smile.gif. W ten sposób otrzymuje jego metody do walidacji i po robocie tongue.gif
Banalny przykład:
  1. <?php
  2. interface iValidate{
  3. public function valid($var);
  4. }
  5.  
  6. class Validator implements iValidate{
  7. public function valid($var){
  8. if(ctype_alpha($var)){
  9. return true;
  10. }
  11. return false;
  12. }
  13. }
  14.  
  15. class Example extends Validator{
  16. protected $var = '';
  17.  
  18. public function __set($name,$value){
  19. if(!$this->valid($name)){
  20. throw new Exception('Nazwa "'.$name.'" nie przechodzi walidacji, więc zmienna nie może być edytowana!');
  21. }
  22. if(!isset($this->{$name})){
  23. throw new Exception('Wartość "'.$name.'" nie istnieje, więc nie może być edytowalna!');
  24. }
  25. $this->{$name} = $value;
  26. }
  27.  
  28. public function __get($name){
  29. if(!$this->valid($name)){
  30. throw new Exception('Nazwa "'.$name.'" nie przechodzi walidacji, więc zmienna nie może być pobierana!');
  31. }
  32. if(!isset($this->{$name})){
  33. throw new Exception('Wartość "'.$name.'" nie istnieje, więc nie może być pobierana!');
  34. }
  35. return $this->{$name};
  36. }
  37. }
  38. try{
  39.  
  40. $o = new example();
  41.  
  42. $o->var = 'Jakieś tam bzdury!';
  43.  
  44. echo $o->var;
  45.  
  46. $o->inna1 = 'Dupa'; 
  47. }
  48. catch(Exception $e){
  49. echo $e->getMessage();
  50. }
  51. ?>
mike
Cytat(Turgon @ 17.03.2007, 10:34:08 ) *
I apropos walidacji. Można to jeszcze prościej. Klasa dziedziczy po Validatorze smile.gif. W ten sposób otrzymuje jego metody do walidacji i po robocie tongue.gif
No bez jaj. Toż to głupota.
A jeśli moja klasa to Samochód dziedzicząca po Pojazd ?
Dorzucanie takich mechanizmów za pomocą dziedziczenia to efekt niezrozumienia idei obiektowości i dziedziczenia.
Turgon
mike_mech: Nie obchodzą mnie idee. To ma działać szybko i jak należy. Jak chcesz być tak bardzo obiektowy. Dodaj mechanizm dodawania walidatorów
mysz
@Turgon: tutaj się przyłączam do mike_mech: średnie rozwiązanie. chcesz w ten sposób robić osobne klasy do sprawdzania każdej właściwości? Czy też różne interfejsy? Bez sensu. To akurat lepiej jest zrealizować w poszczególnych metodach.
athabus
Ja osobiście uważam, że metody __get i __set są raczej kiepskim wyborem. Jak dla mnie są bardzo nieczytelne - zawsze uważałem, że klasa jednoznacznie musi definiować swoje API - jeśli mam dość skomplikowaną klasę z kilkoma polami różnego typu to wkrada się tam zbyt duża komplikacja - zbyt dużo if'ów itp. W zasadzie tylko kilka razy udało mi się znaleźć rozsądne zastosowanie do metod magicznych - w większości przypadków preferuje zwykłe setZmienna() i getZmienna() gdyż od razu widze, które pola były przewidziane do publicznego dostępu. Choć w początkowej fazie narzuca to więcej bezsensownego klepania to potem jest znacznie wygodniejsze w użytkowaniu i łatwiejsze w dokumentacji.
Turgon
Mysz: Rozwiązanie najprostsze =] Pisane z palca tongue.gif Bez konkretnego zastosowania. Ja inaczej to rozwiązuje u mnie w klasach winksmiley.jpg Z resztą __set i __get są niesamowicie przydatne przy interfejsie obiektowym np. dla danych usera winksmiley.jpg
mysz
@athabus: właśnie w tym rzecz: Ty stawiasz na czyelność kodu, ja stawiam na elegancję i naturalność używania. No i kwestia tego, że dla mnie taki szkielet jak zaprezentowałem na początku jest czytelny i nie narzekam, ale to już indywidualna kwestia :)

@Turgon: Ja nie neguję używania metod magicznych, wręcz czasem je nadużywam, ale są dla mnie niezmiernie wygodne. Chodziło mi tylko o metodę walidacji przedtsawiona przez Ciebie powyżej :)
Strzałek
Cytat
mike_mech: Nie obchodzą mnie idee. To ma działać szybko i jak należy. Jak chcesz być tak bardzo obiektowy. Dodaj mechanizm dodawania walidatorów


WOW. Dobra to powiedz mi ile tych cennych 0,0001 sek. tracisz i ile takich wielkich projektów napisałeś żebyś musiał aż tak oszczędzać na czasie, co bardziej jest niewygodną niż oszczędnością ?
menic
Ja preferuje metody getXXX setXXX. Bardzo zwieksza czytelnośc kodu. Ale nie jestem też przeciwnikiem metod magicznych. Co jedne bedzie dobre w konkretnym przypadku, to drugie niekoniecznie. Trzymanie sie na siłe jednego, jest ble tongue.gif
Turgon
Strzałek: No-comment smile.gif Dla mnie to jest prostsze. Jak chce pisać coś czytelniejszego niż kodzik na kilka godzin, to piszę w innym stylu.
DjKermit
Nie wiem czy wiecie ale w pewnym języku jest to rozwiązane IMO dużo lepiej niż w PHP
Kod
class Foo {
    
    private var _foo;
    
    public function get foo() }
        return _foo;
    }

    public function set foo(val) {
        // filtry
        _foo = val;
    }
    
}

Moim zdaniem genialne rozwiązanie i bardzo mi tego brakuje w pehapie, natomiast w owym języku brakuje mi magicznych __get i __set do wyłapywania odwołań do nieistniejących zmiennych.
Było by idealnie połączyć obie metody.
mysz
@DjKermit: w jeszcze innych językach jest to też zupełnie inaczej rozwiązane, i też dobrze działa. Ale to bodajże forum PHP?
DjKermit
Cytat(mysz @ 17.03.2007, 12:59:32 ) *
@DjKermit: w jeszcze innych językach jest to też zupełnie inaczej rozwiązane, i też dobrze działa. Ale to bodajże forum PHP?

To napisz do Zend'a żeby wykorzystali tą metodę, wtedy będzie na temat.
A ja myślałem że rozmawiamy o setter/getter.
Jak Cię nie interesuje mój post, to go pomiń i nie czytaj, nie pisałem tylko do Ciebie.
marcini82
No to widze, ze zdania sa bardzo podzielone snitch.gif
Osobiscie mam wrazenie, ze wszystko zalezy od konkretnej sytuacji i od tego, jakie jeszcze dodatkowe zadania chcielibysmy wykonywac przy okazji pobierania/ustawiania wlasciwosci.
Jestem jednak raczej przeciwnikiem klepania setterow i getterow wszedzie gdzie popadnie. Czesto chyba lepiej ich nie tworzyc, a jesli zajdzie potrzeba to dopisac __set() i __get().

Mysz napisal:
Cytat
Nie mówiąc o tym jak wygodnie jest to rozwiązane w Pythonie czy C#, ale to już drobny szczegół...

Tak z ciekawosci, mozesz rzucic przykladem? Sam napisales ze to forum PHP, ale dobrze czasem porownac jak robi sie rozne rzeczy gdzie indziej.
em1X
Cytat(menic @ 17.03.2007, 12:10:22 ) *
Ja preferuje metody getXXX setXXX. Bardzo zwieksza czytelnośc kodu.


Rozwiązanie rodem wyciągnięte z pięknej Javy. Popieram przedmówcę, jednak sposobów programowania jest tyle co samych programistów..
mysz
Cytat(DjKermit @ 17.03.2007, 13:21:31 ) *
Jak Cię nie interesuje mój post, to go pomiń i nie czytaj, nie pisałem tylko do Ciebie.


E, nie chciałem żebyś tak to odebrał, chciałem nakierować dyskusję na bardziej pehapowe tory :/

Cytat
Moim zdaniem genialne rozwiązanie i bardzo mi tego brakuje w pehapie, natomiast w owym języku brakuje mi magicznych __get i __set do wyłapywania odwołań do nieistniejących zmiennych.
Było by idealnie połączyć obie metody.


Python na to pozwala :) Ale to też offtopicznie... ;)

Cytat
Cytat
Nie mówiąc o tym jak wygodnie jest to rozwiązane w Pythonie czy C#, ale to już drobny szczegół...


Tak z ciekawosci, mozesz rzucic przykladem? Sam napisales ze to forum PHP, ale dobrze czasem porownac jak robi sie rozne rzeczy gdzie indziej.


Python ma funkcję property() (przykład pod linkiem), a w C# mniej więcej (podobnie jak w przykładzie podanym przez DjKermita):
Kod
class Test {
  private int a;
  public int A {
    get { return a; }
    set { a = value; }
  }
}


Pozwala to na rozdzielenie walidacji na różne funkcje bez cyrkowania i sprawdzania czy jest jakaś metoda w klasie. Oczywiście jest też możliwość (przynajmniej w pythonie, nie mam pewności czy też w C#) na wyłapywanie nieistniejących właściwości, i robienie z nimi tego samego co przedstawiłem wcześniej w przykładzie dla PHP.
menic
Jeżeli chodzi o "wyłapywanie" nieistniejacych metod wystarczy __call() i bedzie to samo winksmiley.jpg
DjKermit
Ja jeszcze czasami używam czegoś takiego
  1. <?php
  2. class Foo {
  3.  
  4. private $foo;
  5.  
  6. public function foo($value = null) {
  7. if (! is_null($value)) {
  8. // filtry
  9. $this->foo = $value;
  10. }
  11. return $this->foo;
  12. }
  13. }
  14. ?>
Hacker
Hmmm... Jakoś ja nie widzę problemu. Można przecież zrobić __get i __set, a w nich sprawdzać tylko czy do danej zmiennej można się odwołać i wykonuje getXXX albo setXXX, gdzie jest reszta dokładne sprawdzanie czy podane są dobre dane itp. dodatkowo __call i jazda. I wtedy można i $obiekt->zmienna i $obiekt->getZmienna()
Sedziwoj
Cały kłopot jest w tym kto w czym zaczął programować i jak podatny jest na zmiany.
Bo w Java nie ma możliwości wykonania jakiś operacji przy odwołaniu do zmiennej, dlatego stosuje się setZmienna() i getZmienna(), a że czasem później się okazują potrzebne to się daje od razu wszędzie 'na wszelki wypadek', bo potem przy zmianie interfejsu klasy trzeba było by przerabiać kod wszędzie gdzie są odwołania do tej zmiennej.
Tu PHP ma plusa (nie tak jak wspomniany Python) bo możemy to kontrolować, i można dodać to później, a nie będzie miało to wpływu na resztę kodu, co jest chyba jednych ważniejszych rzeczy?

Dlaczego uważam za zbędne, a nawet nie wskazane, używanie getZmienna(), setZmienna() bo tworzy to sztuczne metody, do tego czasem niewykorzystywane. Tworzyć na przód takie metody, bo mogą być potrzebna, tworzy nadmiarowy kod. Dzięki __set() i __get() mamy możliwość wprowadzania sprawdzenia, do tego nie widocznego przy użyciu.

To co napisał Hacker można użyć, do produkcji "deprecate", bo ostatecznie powinno się przejść na __get() i __set().
Ale to moja opinia, ilu programistów... ale radził bym się zastanowić, bo podejście 'mi się to nie podoba, więc jest złe' jest takie trochę dziecinne.
DjKermit
Cytat(Hacker @ 29.03.2007, 18:58:26 ) *
Hmmm... Jakoś ja nie widzę problemu. Można przecież zrobić __get i __set, a w nich sprawdzać tylko czy do danej zmiennej można się odwołać i wykonuje getXXX albo setXXX ...


A to bardzo ciekawe, bo __get jest wykonywane TYLKO wtedy gdy wywołany atrybut NIE istnieje.
Hacker
Cytat
A to bardzo ciekawe, bo __get jest wykonywane TYLKO wtedy gdy wywołany atrybut NIE istnieje.


Lub jest niewidoczny (private, protected)
Sedziwoj
Hacker mnie uprzedził biggrin.gif
Czyli wystarczy zmienić z public na protected lub private i już przejmują obsługę __set(), __get(), __unset() i __isset(). Tak wiec niczym się nie różni od np. getZmienna() bo ta Zmienna też musiała być niewidoczna.
DjKermit
Cytat(Hacker @ 30.03.2007, 13:42:55 ) *
Lub jest niewidoczny (private, protected)

OK, to zwracam honor i ubolewam nad moją niewiedzą.
Sedziwoj
Powiązane z tematem, bo jak sprawdzić czy mamy daną zmienną czy też nie w obiekcie...
Można get_object_vars() czy get_class_vars(), a dlaczego w ogóle nad tym myślałem, bo isset() zwraca false jeśli zmienna jest NULL... Więc nie widzę metody aby sprawdzić czy zmiennej nie ma czy jest ustalona na NULL (oprócz wymienionych get_...).
A co ciekawego zauważyłem, że is_null($this->zmienna) sprawdza w obiekcie zmienną, tylko że jak jej nie znajdzie odpala __get() (czyli jak mamy nadpisane, bo przy domyślnym to nic nie zmienia) i sprawdza co zwraca biggrin.gif
To samo isset() [zapewne wszystkie tego typu], sprawdzają przy $this->zmienna, najpierw czy taka zmienna jest jeśli nie, to odpalają zewnętrzne pobieranie zmiennych, czyli nasze nadpisane __get() itd. smile.gif

Chyba to może być przydatne... ale w praktyce jedynie wyjdzie.

(ciekawe czy się nie zbłaźniłem tym 'odkryciem', jak to już raz zrobiłem)

EDIT literówka
Hacker
No, cóż... Jakby tu powiedzieć.
Niestety nic nie odkryłeś...
nie is_null($this->zmienna) wywołuje __get (gdy nie znajdzie zmiennej w obiekcie), a samo $this->zmienna (zwłaszcza jeżeli zmienna nie jest obiektem to można się tego łatwo domyślić (PHP przekazuje przez wartość, a nie referencje))
Sedziwoj
Właśnie mnie coś męczyło, co się okazało, jak zwykle nie doczytaniem w manualu (będę musiał go strona po stronie przeczytać w końcu).
Chodzi przy __set(), bo jak umieszczamy w tablicy to array_key_exists() lub jak chcemy to dodajemy [teraz doczytałem, wiedziałem że musi istnieć] property_exists() czy dana zmienna 'jest w klasie'.
Męczyło mnie to bo ta funkcja nie pojawiała się, a pojawiło isset() które przecież zwróci FALSE jeśli zmienna jest NULL, a przy __set() nie o to nam chodziło.
menic
Ja wszystkie własciwosci, które sa dostepne dla "klienta" wole sobie opakowac w getXXX itp, nawet jesli jest to zwykle return $this->zmienna. IMHO o wiele bardziej zwieksza to czytelnosc kodu i jest "bardziej obiektowe". Wiadomo co do czego. __set, __get to tak jak by małe zerwanie z OO. Ale to tylko moje zdanie winksmiley.jpg
athabus
@menic - mam dokładnie takie same odczucia jak Ty - trzeba jasno zdeklarować interfejs każdej klasy. Przy zastosowaniu __set i __get taki interfejs jest nieczytelny - nie wiadomo co ma być publiczne a co prywatne.

Mi brakuje jedynie jakiegoś odpowiednika właściwości z C#, żeby nie trzeba było dla każdej zmiennej publicznie dostępnej walić 2 funkcji.
marcini82
A ja wlasnie trafilem na sytuacje, w ktorej dostep do wlasciwosci przez getZmienna() i setZmienna() skomplikowalby mi kod korzystajacy z klasy i przysporzyl dodatkowej pracy. Otoz przekazuje dane obiektu do systemu szablonow, ale chce to zrobic w formie tablicy, a nie samego obiektu (zeby mi ktos nie wpisal w szablonie np. $obiekt->delete() tongue.gif ).
No i robie:
  1. <?php
  2. $tablicaWlasciwosci = get_object_vars($obiekt);
  3. ?>

i po krzyku, mam tablice z publicznymi wlasciwosciami.
Gdybym mial zamiast tego kazda wlasciwosc pobierac za pomoca getZmienna() i przepisywac po kolei do tablicy, to byloby to troche zmudne...
No fakt, mozna uzyc get_class_methods(), za pomoca tego wyciagnac metody z get na poczatku i jakos to zautomatyzowac, ale i tak bardziej mi sie widzi uzycie __get() i __set() tylko w razie potrzeby i dostep do wlasciwosci...
Sedziwoj
marcini82 to co napisałeś nie jest dla mnie jasne... (może ja nie łapię po prostu) ale wydaje mi się że coś przekombinowałeś.
maryaan
Cytat(marcini82 @ 2.04.2007, 13:56:08 ) *
Gdybym mial zamiast tego kazda wlasciwosc pobierac za pomoca getZmienna() i przepisywac po kolei do tablicy, to byloby to troche zmudne...
a nie mozesz zrobic metody getVArray() ktora zwroci tablice z polami ktore chcesz przekazac do szablonu?
marcini82
Cytat
a nie mozesz zrobic metody getVArray() ktora zwroci tablice z polami ktore chcesz przekazac do szablonu?

Moge, oczywiscie smile.gif Moge dodac jeszcze 5 innych, tylko gdzies jest granica pomiedzy oplacalnoscia czasu zainwestowanego w kodowanie a sztuka dla sztuki... Jak pisze jakas bardzo uniwersalna klase to ok, moge w nia zainwestowac. Ale jak tworze w aplikacji kolejny obiekt, i kolejny i kolejny, to wolalbym jednak nie klepac w kazdym z nich serii podobnych metod... A niestety gettery i settery i ew. inne poboczne metody sa uzaleznione od wlasciwosci danej klasy i musza byc tworzone indywidualnie...

Cytat
marcini82 to co napisałeś nie jest dla mnie jasne... (może ja nie łapię po prostu) ale wydaje mi się że coś przekombinowałeś.

Po prostu przepisuje publiczne wlasciwosci obiektu do tablicy. W momencie, gdy sa one dostepne tylko za pomoca metod get..(), to jest to odrobine utrudnione i mniej wygodne.

Ale jak kto lubi...
Sedziwoj
Cytat(marcini82 @ 4.04.2007, 08:06:35 ) *
Po prostu przepisuje publiczne wlasciwosci obiektu do tablicy. W momencie, gdy sa one dostepne tylko za pomoca metod get..(), to jest to odrobine utrudnione i mniej wygodne.


Wiesz ja wiem co robi get_object_vars() mi chodzi co chcesz przez to osiągnąć...
marcini82
Cytat
Wiesz ja wiem co robi get_object_vars() mi chodzi co chcesz przez to osiągnąć...

Przekazac do szablonu tablice z wlasciwosciami obiektu. Nie chce przekazac obiektu, zeby nie dawac szablonowi dostepu do jego metod. Wiec przepisuje wlasciwosci do tablicy i przekazuje tablice.
Nie wiem czy to wystarczajaco jasne...
NoiseMc
Z zasady pola klasy powinny byc prywatne i nie powinno sie do nich odwolywac bezposrednio lecz przez gettery i settery. Nie spotkalem sie do tej pory z problemem, ktory potwierdzilby ze zasady tej nalezy przestrzegac wiem natomiast ze powinno sie.
Zazwyczaj utworzony obiekt przekazuje do obiektu Smarty, a ten sobie odczytuje pola poprzez {$obiekt->pole} i to jest bardzo wygodne aczkolwiek zgodnie z zasadami hermetyzacji nie powinienem robic tego w ten sposob.

Pomyslalem sobie ... czemu by nie zrobic tak:
- ustawic wszystkie pola jako prywatne
- dopisac do kazdej klasy __get() z kodem:

  1. <?php
  2. public function __get ($name)
  3. {
  4. $functionName = 'get' . ucfirst ($name);
  5.  
  6. $isDeclared = in_array ($name, array_keys (get_class_vars (get_class ())));
  7.  
  8. if (! $isDeclared)
  9. {
  10. throw new Exception (get_class () . '::$' . $name . ' no such a property');
  11. }
  12.  
  13. if (method_exists (get_class (), $functionName))
  14. {
  15. return $this->$functionName ();
  16. }
  17. else
  18. {
  19. throw new Exception ('Cannot access private property ' . get_class () . '::$' . $name);
  20. }
  21. }
  22. ?>


Oczywiscie do tych wlasciwosci, ktore chce wystawiac na zewnatrz pisze getVar i setVar dzieki czemu hermetyzuje pola ... dodatkowo pisze jeszcze metody dostepu dla ... na przyklad szablonow Smarty.
W ten sposob moglbym odwolywac sie do pol tak jak robie to do tej pory trzymajac sie przy tym zasad hermetyzacji.

Co myslicie na ten temat?

PS: Mike dzieki za przeniesienie smile.gif
Whisller
@NoiseMC
A jak to sie ma z wydajnoscia? Poniewaz troche duzo tych funkcji tutaj, get_class, get_class_vars, in_array i tak dalej.
Do tego co się orientowałem(nie sprawdzałem w własnych testach) __get jest wolniejsze ( dzięki splatch smile.gif ).

I kolejna sprawa, odwolujesz się do metod publicznych poprzez __get, tak? Które zwracają zmienne prywatne? W takim razie po co korzystać z __get? Skoro możesz po prostu użyć $myObject->getName() etc. Bo chyba nie do końca rozumiem Twojej idei.
Cysiaczek
Jak podejrzewam, że obiekt będzie miał dużo setterów i getterów, to:

  1. <?php
  2. public function __call($method, $args)
  3. {
  4.  
  5. if(!method_exists($this, $method))
  6. {
  7.  
  8. $pre=substr($method, 0, 3);
  9. $post=substr($method, 3);
  10. $propertyName=lcfirst($post);
  11.  
  12. if($this->has($propertyName))
  13. {
  14. if($pre=='set')
  15. {
  16. return $this->setProperty($propertyName, $args[0]);
  17. }
  18. if($pre=='get')
  19. {
  20. return $this->getProperty($propertyName);
  21. }
  22. }
  23. }
  24.  
  25. return call_user_func_array(
  26. array($this, $method),
  27. $args
  28. );
  29. }
  30. ?>


Pozdrawiam.
NoiseMc
Cytat
Pisząc w JSP możesz bez problemu korzystać z ${obj.property}, co w wyniku, po skompilowaniu do servletu da Ci obj.getProperty().


Chodzilo mi o to zebym mogl z poziomy szablonu wyciagac sobie w ten sposob wartosci pol czyli jezeli wolam {$obj->property} to klasa sprawdza czy ma getProperty (), jezeli ma to z niej korzysta jezeli nie to wyrzuca wyjatek ze zmienna jest prywatna i sio od niej, tak sobie tylko mysle.
Co do wydajnosci, nie wiem czy to ma jakiekolwiek znaczenie ... w koncu nie pisze sterownika, wieksze problemy mialem przy optymalizacji zapytan albo rekurencyjnym przetwarzaniu duzych tablic winksmiley.jpg
splatch
Stosunkowo nieinwazyjne dorzucenie walidacji z użyciem metod set/get. Oczywiście pytanie czy walidacja powinna być przesunięta tak nisko, czy też powinny załatwiać ją kontrolery pomijam. To jest po prostu przykład. Warto zauważyć, że jest to coś innego niż Obserwator i nie wymaga implementacji żadnych dodatkowych interfejsów od klasy reprezentującej obiekty biznesowy.

  1. <?php
  2. final class PropertyChangeEvent {
  3. private $source;
  4. private $property;
  5. private $newValue;
  6. private $oldValue;
  7.  
  8. public function __construct($source, $property, $newValue, $oldValue) {
  9. // przypisanie wartości
  10. }
  11. }
  12.  
  13.  
  14. // Interfejs dla klas, które są zainteresowane zmianami wartości
  15. interface PropertyChangeListener {
  16. public function propertyChange(PropertyChangeEvent $e);
  17. }
  18.  
  19. // Typowy mediator pośredniczący w propagowaniu z mian z $source do $listeners
  20. class PropertyChangeSupport {
  21. private $listeners;
  22. private $source;
  23.  
  24. public function __construct($object) {
  25. // assert $object != null
  26. if ($object == null) {
  27. throw new UnknowSourceException();
  28. }
  29. $this->source = $object;
  30. }
  31.  
  32. public function addListener(PropertyChangeListener $listener) {
  33. $this->listeners[] = $listener;
  34. }
  35.  
  36. public function removeListener(PropertyChangeListener $listener) {
  37. foreach ($this->listeners as $key => $value) {
  38. if ($value == $listener) {
  39. unset($this->listeners[$key]);
  40. }
  41. }
  42. }
  43.  
  44. public function firePropertyChange($property, $newValue, $oldValue) {
  45. $event = new PropertyChangeEvent($this->source, $property, $newValue, $oldValue);
  46. }
  47. }
  48.  
  49. // Tu nasza klasa
  50. class Book {
  51. private $title;
  52. private $support;
  53.  
  54. public function __construct() {
  55. $this->support = new PropertyChangeSupport($this);
  56. }
  57.  
  58. public function setTitle($newTitle) {
  59. $this->support->firePropertyChange('title', $newTitle, $this->title);
  60. $this->title = $newTitle;
  61. }
  62.  
  63. public function addListener(PropertyChangeListener $listener) {
  64. $this->support->addListener($listener);
  65. }
  66. }
  67.  
  68. // Przykład walidacji - w przypadku niepowodzenia leci wyjątek.
  69. class ValidatorListener implements PropertyChangeListener {
  70.  
  71. public function propertyChange(PropertyChangeEvent $event) {
  72. if ($event->getProperty() == "title") {
  73. if (strlen($event->getNewValue()) == 0) {
  74. throw ValidationException($event->getProperty());
  75. }
  76. }
  77. }
  78.  
  79. }
  80.  
  81. $book = new Book();
  82. $book->addListener(new ValidatorListener());
  83. $book->setTitle("a");
  84. $book->setTitle("");
  85.  
  86. // a tu inny przykład, który można uzyskać
  87. class CompositeValidatorListener implements PropertyChangeListener {
  88. // Map<String, List<PropertyChangeListener>>
  89. private $validators;
  90.  
  91. public function put($property, PropertyChangeListener $listener) {
  92. if ($this->validators[$property] == null) {
  93. $this->validators[$property] = array();
  94. }
  95. $this->validators[$property][] = $listener;
  96. }
  97.  
  98. public function propertyChange(PropertyChangeEvent $event) {
  99. $prop = $event->getProperty();
  100. if (isset($this->validators[$prop])) {
  101. foreach ($this->validators[$prop] as $listener) {
  102. $listener->propertyChanged($event);
  103. }
  104. }
  105. }
  106. }
  107.  
  108. // Walidacja z użyciem maski
  109. class RegexpValidator implements PropertyChangeListener {
  110. public function __construct($regexp) {
  111. $this->regexp = $repexp;
  112. }
  113.  
  114. public function propertyChange(PropertyChangeEvent $event) {
  115. if (!preg_match($this->regexp, $event->getNewValue())) {
  116. throw new ValidationException($event->getNewValue());
  117. }
  118. }
  119. }
  120.  
  121. $composite = new CompositeValidatorListener();
  122. $composite->put('age', new RegexpValidator('/d+/'));
  123. $composite->put('name', new RegexpValidator('/w+/'));
  124.  
  125. $author = new Author();
  126. $author->addListener($composite);
  127.  
  128. ?>
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.