Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [php 5.1] Pisać osobne klasy czy dziedziczenie?
Forum PHP.pl > Forum > PHP > Object-oriented programming
szczurek
Witam,

Mam pytanie. Na stronie będę musiał zarządzać takimi częściami jak Uzytkownik, Autor, Utwor, Komentarz, News

No i dla każdego zamierzam napisać klasę z metodami tylko, że poza innimi metodami wszystkie zawierają wspólne:
  • doaj
  • pobierz
  • aktualizuj
  • usun

Jeszcze sprawdz dane, ale to będzie na pewno metoda prywatna.

Zastanawiam się czy mam kombinować z napisaniem jakiejś klasy zawierającej te 4 metody i później klasy uzytkownik, autor itd. Mają dziedziczyć z tej klasy modyfikując właściwie pola które dodają czy pobierają czy każda klasa ma zawierać pisane od początku metody i tylko mają implementować interface który zawiera abstrakcyjne metody dodaj, pobierz, aktualizuj i usun.
cadavre
Sądzę, że te metody będą zajmować się zupełnie czym innym (dodanie usera, dodanie newsa) więc myślę, że nie wiele wspólnego będą mieć. Ty powinieneś znać logikę swojej aplikacji. smile.gif
batman
Odpowiedź na swoje pytanie znajdziesz w moim projekcie konkursowym winksmiley.jpg
A tak na poważnie. Tu masz przykład jak może wyglądać klasa, po której mogą dziedziczyć Twoje klasy (wyjęte na żywca z projektu):

  1. <?php
  2. /**
  3.  * Klasa Fieldset
  4.  *
  5.  * Klasa udostepniająca jednolity interface do pobierania i modyfikacji danych. 
    Nie można bezpośrednio utworzyć obiektu klasy. Jedynie przez klasę potomną.
  6.  * Klasy nie się zastosować w przypadu tabel - relacji, w których nie ma jasno określonego klucza podstawowego. Rozwiązaniem t
    ego problemu może być
  7.  * utworzenie widoku.
  8.  *
  9.  * @author Maciej "Batman" Wilgucki
  10.  * @package DataList 
  11.  *
  12.  */
  13. abstract class Fieldset
  14. {
  15. /**
  16.  * Nazwa tabeli, którą wykorzystuje klasa
  17.  *
  18.  * @var string
  19.  */
  20. protected $stable;
  21.  
  22. /**
  23.  * Nazwa pola będącego kluczem podstawowym
  24.  *
  25.  * @var string
  26.  */
  27. protected $idfield;
  28.  
  29. /**
  30.  * Wartość pola będącego kluczem podstawowym
  31.  *
  32.  * @var mixed
  33.  */
  34. protected $idvalue;
  35.  
  36. /**
  37.  * Flaga określająca, czy dane zostały już załadowane
  38.  *
  39.  * @var bool
  40.  */
  41. protected $bloaded = false;
  42.  
  43. /**
  44.  * Tablica asocjacyjna zawierająca wiersz o identyfikatorze określonym przez $id
    value
  45.  *
  46.  * @var array
  47.  */
  48. protected $arow = array();
  49.  
  50. /**
  51.  * Flaga określająca, czy dokonano modyfikacji danych
  52.  *
  53.  * @var bool
  54.  */
  55. protected $bmodified = false;
  56.  
  57. /**
  58.  * Tablica asocjacyjna zawierająca zmodyfikowane pola
  59.  *
  60.  * @var array
  61.  */
  62. protected $amodified_fields = array();
  63.  
  64. /**
  65.  * Metoda, która powinna zwracać kod SQL odpytujący bazę pod kątem ilości wiersz
    y w tabeli. Musi zostać zadeklarowana w klasie potomnej
  66.  */
  67. abstract public function getTotal();
  68.  
  69. /**
  70.  * Metoda, która powinna zwracać kod SQL zwracający dane z bazy danych. Musi zos
    tać zadeklarowana w klasie potomnej
  71.  */
  72. abstract public function getList();
  73.  
  74. /**
  75.  * Inicjalizacja obiektu (nie mylić z konstruktorem). Metoda ta musi być wywołana, nim jakiekolwiek inne operacje na danych będ
    ą wykonywane.
  76.  * W metodzie tej ustawiane są nazwa tabeli, nazwa pola będącego kluczem podstaw
    owym oraz opcjonalnie wartość tego pola
  77.  *
  78.  * @param string nazwa tabeli
  79.  * @param string nazwa pola będącego kluczem podstawowym
  80.  * @param string wartość pola będącego kluczem podstawowym  
  81.  * @return void
  82.  */
  83. public function init($stable,$idfield,$idvalue=0) {
  84. $this->stable = $stable;
  85. $this->idfield = $idfield;
  86. $this->idvalue = (int)$idvalue;
  87. }
  88.  
  89. /**
  90.  * Zapisanie wiersza, jeśli jeszcze to nie miało miejsca, do tabeli asocjacyjnej
    . Jeśli wiersz został pobrany, 
  91.  * wówczas wszystkie zapytania o wartości poszczególnych pól, będą kierowane do 
    tablicy asocjacyjen $arow.
  92.  *
  93.  * @return void
  94.  */
  95. public function load() {
  96. if(!$this->bloaded) $this->forceLoad();
  97. }
  98.  
  99. /**
  100.  * Wymuszenie pobrania danych z bazy danych. Zawartość tablicy asocjacyjnej $aro
    w zostaje zastąpiona wartościami pobranymi z bazy.
  101.  * Nie powinno się używać tej metody do ładowania danych, ponieważ każde jej wyw
    ołanie powoduje wykonanie zapytania do bazy danych,
  102.  * co może spowodować spadek wydajności.
  103.  *
  104.  * @return void
  105.  */
  106. public function forceLoad() {
  107. if(empty($this->stable) || empty($this->idfield) || empty($this->idvalue))
  108. throw new FieldSetException("You can't load data with empty params. Use init first!");
  109.  
  110. $sql = "select * from ".$this->stable." where ".$this->idfield." = ".$this->idvalue;
  111. $Connection = Connection::Main();
  112. $tab = $Connection->getRows($sql);
  113. $this->arow = $tab[0];
  114. $this->bloaded = true;
  115. }
  116.  
  117. /**
  118.  * Pobranie wartości pola bazy danych
  119.  *
  120.  * @param string nazwa pola z tabeli, którego wartość ma zostać zwrócona
  121.  * 
  122.  * @return string
  123.  */
  124. public function getField($sname) {
  125. if(!$this->bloaded) $this->load();
  126. if(!array_key_exists($sname, $this->arow))
  127. throw new FieldSetException("There is no <b>$sname</b> field in ".$this->stable);
  128. return $this->arow[$sname];
  129. }
  130.  
  131. /**
  132.  * Zapisanie nowej wartosci pola. By dane zostały zapisane do bazy należy wywoła
    ć metodę save
  133.  * @see save  
  134.  *
  135.  * @param string nazwa pola z tabeli, którego wartość ma zostać zapisana
  136.  * @param string wartość, która ma zostać zapisana
  137.  * 
  138.  * @return void
  139.  */
  140. public function setField($sname, $svalue) {
  141. $this->bmodified = true;
  142. $this->amodified_fields[$sname] = $svalue;
  143. }
  144.  
  145. /**
  146.  * Zapisuje do bazy danych zmiany dokonane w wiersdzu. Jeśli dane nie zostały wc
    zesniej załadowane lub nie jest okreslona wartość
  147.  * pola bedącego kluczem podstawowym, wówczas dane dodawane są do tabeli. W prze
    ciwnym razie - modyfikowane.  
  148.  *
  149.  * @return void
  150.  */
  151. public function save() {
  152. if($this->bmodified) {
  153. $Connection = Connection::Main();
  154. if($this->bloaded && $this->idvalue > 0)
  155. $Connection->update($this->stable,$this->amodified_fields,"where ".$this->idfield." = ".$this->idvalue);
  156. else
  157. $Connection->insert($this->stable,$this->amodified_fields);
  158. }
  159. }
  160. }
  161. ?>
Cysiaczek
@szczurek

Moim zdaniem najlepsza jest kompozycja w tym wypadku, zatem lepiej jak klasy będą implementowały interfejs.

Pozdrawiam.
ActivePlayer
@batman opisywalem tą metodę w phpsolutions 04/2006, podałem tam gotowy kod klasy, razem z rozwiązaniem lazy init'a, finnderów danych itp
batman
@ActivePlayer
Tak się składa, że nie czytuję phpsolutions. A klasę sam napisałem, nie wzorując się na niczyich przykładach.
athabus
Ja zgodzę się z Cysiaczkiem - najlepiej w takich przypadkach zazwyczaj użyć Interfejsu - wtedy możesz w razie konieczności sprawdzać czy dany obiekt implementuje dany interfejs (np. gdy gdzieś w innym miejscu aplikacji będzie metoda mogąca brać dowolne obiekty, które implementują te metody).

Z Twojego opisu sytuacji wnioskuje jednak, że poza takimi samymi nazwami metod nie idzie za nimi większa abstrakcja, więc pytanie czy w ogóle jest sens tutaj wprowadzać jakiekolwiek zależności.

Pamiętaj, że dziedziczenie nie powstało po to aby "zaoszczędzić pisania", a żeby pozwalać na różne poziomy abstrakcji i ułatwiać pisanie/rozbudowywanie kodu. Dziedziczenie na siłę klas, które nie są logicznie powiązane powoduje zazwyczaj tylko bałagan w kodzie i problemy z jego utrzymaniem.

//edit

No może tutaj można zrobić specjalny obiekt, który będzie operował na bazie i będzie pozwalał na dodawanie / usuwanie /modyfikowanie/odczytywanie poszczególnych danych - coś jak Active Record Pattern
dr_bonzo
Przy ActiveRecord dziedziczenie ulatwia sprawe -- definiujesz pola i ich typy w podklasach a nadrzedna klasa zajmuje sie ich zapisem i odczytem.
szczurek
Dzięki wielkie za odpowiedzi, zdecydowałem się na użycie interface'u.

Tylko oczywiście "znowu w życiu mi nie wyszło.

Jak już ktoś zauważył metody różnych klas będą miały różne pola w bazie, no więc i różne parametry(mógłbym przyjmować tablice jako jeden parametr, ale nie widzę w tym większego sensu).

Napisałem więc interface

  1. <?php
  2. interface nazwa{
  3. function Dodaj();
  4.  
  5. (Pozosta&#322;e metody tak samo)
  6. }
  7. ?>


W klasie jest np.

  1. <?php
  2. class Uzytkownik implements nazwa{
  3. public function Dodaj($param, $param2)
  4. return true;
  5. }
  6. ?>


No i niestety wywala mi kominukat że deklaracja musi być zgodna z interface'm

Jeśli w interface będzie

  1. <?php
  2. interface nazwa{
  3. public function Dodaj($param1, param2);
  4. }
  5. ?>


To oczywiście nie ma problemu. Ale ja nie mogę jasno orkęslić jakie to będą paramerty(chyba, że jak pisałem tablica).


Mógłbym napisać abstrakcyjną klasę, no ale jak mówiłem zdecydowałem się na interface, po waszych wypowiedziach doszedłem do wniosku, że w moim przypadku to będzie lepsze.
dr_bonzo
W twoim przypadku wspolny interfejs Dodaj(...) jest nieprzydatny bo nie jest wspolny smile.gif kazda klasa musi u ciebie miec inna liste parametrow, a zadeklarowanie tych metod jako interfejs nic nowego nie wnosi.

Mozesz np zrobic tak (jak jest np w RoR), wzorzec ActiveRecord:

  1. <?php
  2. $user = new User();
  3. $user->setName( 'Bob' );
  4. $user->setAge( 23 );
  5. ...
  6. $user->save();
  7. ?>


i dopisz do tego implementacje
szczurek
Dzięki wielkie, pozdrawiam.
athabus
Musisz zadać sobie pytanie: "po co ja chcę to zrobić". Użycie interfejsu ma sens jeśli chcesz ujednolicić interfejs - jeśli natomiast to nie jest Twoim celem to jest to używanie mechanizmów obiektowych "na siłę".

Wspólny interfejs jest po to aby potem np. pisać metody, które jako parametr mogą brać obiekty implementujące dany interfejs

np.

  1. <?php
  2. class a
  3. {
  4.  
  5. public static function (ICRUD $obj)
  6. {
  7. //ta metoda moze wykonac operacje na dowolnym obiekcie implemetujacym interfejs IC
    RUD
  8. }
  9. }
  10. ?>


W twoim wypadku, jeśli każda metoda bierze inne parametry trudno będzie pisać taki kod, bo w zasadzie nic poza nazwą nie jest wspólne. Tak jak pisałem wcześniej i to co pisze dr_bonzo - możesz zastosować wzorzec ActiveRecord - ale napisanie dobrej klasy tego typu jest dość trudne. Może popatrz jak robili to inni.
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.