Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Klasa abstrakcyjna + składowe statyczne
Forum PHP.pl > Forum > PHP > Object-oriented programming
Capellini
Napisałem sobie pewną klasę abstrakcyjną mającą pewne składowe statyczne:

  1. class class1
  2. {
  3. static $v;
  4. ...
  5. }


Przetestowałem działanie tej klasy, tworząc klasę dziedziczącą:

  1. class class2 extends class1
  2. {
  3. ...
  4. }


... i oczywiście tworząc instancję tej klasy, która dziedziczyła, no i sobie ją przetestowałem. Kiedy już testowanie dobiegło końca, zacząłem używać tej klasy i kiedy utworzyłem dwie klasy dziedziczące po class1 (oczywiście nazwy klas były inne), to okazało się, że coś nie działa.

Nie działało dlatego, że myślałem, że jeżeli w class1 zadeklaruje składową statyczną, to ta wartość będzie taka sama tylko w jednej klasie dziedziczącej, a jest taka sama dla wszystkich klas dziedziczących po class1.

Innymi słowy, spodziewałem się, że jeżeli napisałbym taki kod:

  1. class class1
  2. {
  3. public static $v;
  4. }
  5.  
  6. class class2 extends class1
  7. {
  8. }
  9.  
  10. class class3 extends class1
  11. {
  12. }
  13.  
  14. class2::$v = "abc";
  15. class3::$v = "def";
  16.  
  17. echo(class2::$v);


... to mi wypisze "abc", bo myślałem, że w klasie class2 będzie zapisana wartość "abc", a w klasie class3 będzie "def", a tymczasem class2 i class3 mają tą samą wartość.

Mam takich zmiennych statycznych w tej klasie bardzo dużo więc moje pytanie brzmi, jak najszybciej naprawić ten błąd, żeby nie musieć za dużo pisać od nowa?
blooregard
Cytat
bo myślałem, że w klasie class2 będzie zapisana wartość "abc", a w klasie class3 będzie "def", a tymczasem class2 i class3 mają tą samą wartość.

Nie da rady.
Skoro $v zadeklarowana jest jako statyczna, ma taką samą wartość dla wszystkich obiektów danej klasy. Klasy dziedziczące ją sobie zachowują jej "statyczność", że się tak wyrażę. Istnieje ona w jednym egzemplarzu, wspólnym dla wszystkich klas potomnych i ich obiektów. Zmiana wartości następuje tylko w tym jednym miejscu, więc kod:
  1. class2::$v = "abc";
  2. class3::$v = "def";
  3.  
  4. echo(class2::$v);

wyświetli Ci "def".
marcio
Nie wiem moze powiem herezje ale jak poczytasz o singletonie( aaevil.gif ) to raczej zrozumiesz static dziala mniej wiecej jak stala.
Crozin
Cytat
ale jak poczytasz o singletonie
Jaki to ma związek z tematem?
Cytat
static dziala mniej wiecej jak stala
No i tą herezję powiedziałeś...

Co do tematu: mógłbyś nieco przybliżyć samą ideę kodu? Bo duża ilość składowych statycznych jest coś podejrzana. winksmiley.jpg
marcio
taki ze widac jak dziala static.

Co do stalej moze zle sprecyzowalem ale mniej wiecej jest to to raz deklarujesz i wartosc zostaje taka sama wszedzie.

darko
~blooregard już odpowiedział, tak to działa. Najszybciej byłoby pozamieniać wszystkie pola static na protected i odwoływać się kontekstowo, nadpisując pola chronione (lub ustawiając im nową wartość magicznym setterem). Static to static.
Capellini
Cytat(blooregard @ 20.01.2010, 22:31:25 ) *
Nie da rady.
Skoro $v zadeklarowana jest jako statyczna, ma taką samą wartość dla wszystkich obiektów danej klasy. Klasy dziedziczące ją sobie zachowują jej "statyczność", że się tak wyrażę. Istnieje ona w jednym egzemplarzu, wspólnym dla wszystkich klas potomnych i ich obiektów. Zmiana wartości następuje tylko w tym jednym miejscu, więc kod:
[PHP] pobierz, plaintext
  1. class2::$v = "abc";
  2. class3::$v = "def";
  3. echo(class2::$v);
[PHP] pobierz, plaintext
wyświetli Ci "def".


Cytat(marcio @ 20.01.2010, 23:09:15 ) *
Nie wiem moze powiem herezje ale jak poczytasz o singletonie( aaevil.gif ) to raczej zrozumiesz static dziala mniej wiecej jak stala.


Już teraz zauważyłem, jak działa static, ale w momencie pisania tej klasy - nie. Moje pytanie brzmi: jak najmniejszym kosztem wyeliminować ten błąd?

Cytat(Crozin @ 20.01.2010, 23:16:16 ) *
Co do tematu: mógłbyś nieco przybliżyć samą ideę kodu? Bo duża ilość składowych statycznych jest coś podejrzana. winksmiley.jpg


Klasa nosi nazwę GenericObject i jest głównie czymś w rodzaju ActiveRecord, odpowiada za CRUD itp. Klasami dziedziczącymi po GenericObject mogą być na przykład klasy News albo Komentarz. Obiekty tych klas reprezentują jeden news, albo jeden komentarz.

W klasie GenericObject mam między innymi takie składowe statyczne (i kilka metod statycznych ustawiających te składowe i wykonujące jakieś operacje na tych statycznych) jak:
-> $tableName (+ metody set/getTableName) - nazwa tabeli w bazie danych.
-> $fields - tablica przechowująca dane o poszczególnych polach.
-> statyczna metoda getForm() - pobiera kod HTML generujący formularz do tworzenia/edytowania rekordu.

Cytat(darko @ 20.01.2010, 23:30:14 ) *
~blooregard już odpowiedział, tak to działa. Najszybciej byłoby pozamieniać wszystkie pola static na protected i odwoływać się kontekstowo, nadpisując pola chronione (lub ustawiając im nową wartość magicznym setterem). Static to static.


Nie do końca rozumiem. Czy mógłbyś napisać, co to znaczy "odwoływać się kontekstowo"?
darko
czyli nie statycznie, a na instancji klasy, czyli zamieniasz
  1. class X {
  2. // (...)
  3. public static $v;
  4. // (...)
  5. }

na
  1. class X {
  2. // (...)
  3. protected $v = 5;
  4. // (...)
  5. }

w klasach potomnych (wyprowadzonych) nadpisujesz te pola czy co tam potrzebujesz

  1. class Y extends X {
  2. // (...)
  3. protected $v = 6;
  4. // (...)
  5. }


i po utworzeniu instancji klasy Y dziedziczącej po X:

  1. $y = new Y();


odwołujesz się kontekstowo (przykładowym getterem/setterem):
  1. echo $y->getV();
  2. $y->setV(70);
  3. echo $y->getV();
Crozin
Hmm... jeżeli chodzi o rozwiązanie. Podpatrzyłem jak to dokładnie rozwiązano w ORMie Doctrine. Tam klasy reprezentujące poszczególne tabele (a dokładniej ich rekordy) również dziedziczą po jednej klasie, jednakże schemat tabeli jest ustawiany dla każdego obiektu z osobna:
  1. abstract class BaseNews extends sfDoctrineRecord
  2. {
  3. public function setTableDefinition()
  4. {
  5. $this->setTableName('news');
  6. $this->hasColumn('id', 'integer', 4, array(
  7. 'type' => 'integer',
  8. 'primary' => true,
  9. 'autoincrement' => true,
  10. 'length' => '4',
  11. ));
  12. $this->hasColumn('title', 'string', 255, array(
  13. 'type' => 'string',
  14. 'notnull' => true,
  15. 'length' => '255',
  16. ));
  17. //...
Jednak, jeżeli chcesz by było to statyczne to... i tak musisz w kodzie umieścić przypisanie wartości dla tych składowych, więc notabede dopisanie Na początku protected static $abc; nie będzie takie straszne (szczególnie, że nie będziesz mieć tego aż tak dużo). Zawsze możesz też zrobić to na takiej zasadzie:
  1. abstract class GenericObject {
  2. abstract protected static getTableName();
  3. abstract protected static getTableDefinition();
  4.  
  5. protected static getForm() {
  6. return '....' . self::getTableName() . '....';
  7. }
  8. }
  9.  
  10. class News extends GenericObject {
  11. protected static getTableName() {
  12. return 'News';
  13. }
  14.  
  15. protected static getTableDefinition() {
  16. return array(...);
  17. }
  18. }
darko
~Crozin składowe statyczne nie są dziedziczone w klasach potomnych. abstract protected static <--- to nie ma sensu ! static to static.
l3l0
PHP 5.3 wprowadza "Late static binding", gdzie możesz osiągnąc coś podobnego. Jednak we wcześniejszych wersjach nie jest to możliwe (i moim zadaniem potrzebne też nie jest.. aż tak bardzo tongue.gif )
http://blog.felho.hu/what-is-new-in-php-53...ic-binding.html
Crozin
@darko: odzwyczaiłem się od PHP i zapomniałem wstawić tam jeszcze (kompletnie niepotrzebnego ale to już uroki PHP) słówka function. Jednak z kontekstu i tak da się wywnioskować co miałem na myśli. winksmiley.jpg
wookieb
Cytat(marcio @ 20.01.2010, 23:09:15 ) *
Nie wiem moze powiem herezje ale jak poczytasz o singletonie( aaevil.gif ) to raczej zrozumiesz static dziala mniej wiecej jak stala.

ech...
  1. class c1
  2. {
  3. public static $v = 1;
  4. }
  5.  
  6. class c2 extends c1
  7. {
  8. public static $v = 2;
  9. }
  10.  
  11.  
  12. echo c1::$v;
  13. echo c2::$v;
  14.  
  15. c2::$v = 3;
  16. echo c1::$v;
  17. echo c2::$v;
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.