Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Kiedy używać dziedziczenia ?
Forum PHP.pl > Forum > PHP > Object-oriented programming
Damiankossek
Witam, tak jak w temacie ... Wszedłem w świat OOP jakiś czas temu, teraz robię strony na Kohanie smile.gif Bardzo przypadła mi do gustu ... Możecie powiedzieć mi kiedy używać dziedziczenia ? Wiem że jest to `jakby` rozdrabnianie klas ... Lecz nigdy nie mogłem znależć praktycznego zastosowania dziedziczenia w swoich skryptach smile.gif Prosiłbym kogoś o takie szybkie i łopatologiczne rozpisanie jakiegoś skryptu np. newsów, artykułów czy czego kolwiek (byle nie zwierząt tongue.gif) ... Nie mówię że ma być kod tylko jak to ma wyglądać w praktyce, w formie jakiegoś drzewka lub czego kolwiek smile.gif

No i jeszcze jedno .. Jak dziedziczyć w Kohanie ? Bo widzę że moje wszystkie modele i kontrolery już dziedziczą klasy wyznaczone przez Kohane smile.gif _Models i _Controllers ... Da się jeszcze podpiąć coś czy nie bardzo smile.gif ?

Przepraszam jeżeli są to jakieś durne pytania, ale niepotrafie zastosować tego w praktyce smile.gif

Pozdrawiam
vegelus
Ja ostatnio zbudowałem sobie zestaw do tworzenia słowników z bazy danych w oparciu o własną warstwę abstrakcyjną do łączenia się z bazą.
Problem już wcześniej miałem rozwiązany funkcjami (funkcja do selecta, do multiselect, do checkboxa) ale jakoś mi się to nie podobało i teraz mam jeden mały zestaw:

abstract.menuList.php
  1. abstract class menuList {
  2. protected $sortBy;
  3. protected $sql;
  4. protected $tableName;
  5. protected $fieldName;
  6. protected $idName;
  7. protected $checkBy;
  8.  
  9. private $whereAnd = array();
  10. private $whereOr = array();
  11. private $db;
  12.  
  13. abstract public function PrintList();
  14. abstract protected function prepear();
  15.  
  16. public function __construct($tableName, $fieldName, $idName) {
  17. ...}
  18. protected function getItemDB() {
  19. ...}
  20. protected function addWhereAnd($item) {
  21. ...}
  22. protected function addWhereOr($item) {
  23. ...}
  24. public function sortByName($rodzaj = 'ASC') {
  25. ...}
  26. public function sortById($rodzaj = 'ASC') {
  27. ...}
  28. public function checkById(){
  29. ...}
  30. public function checkByName(){
  31. ...}
  32. }


(na potrzeby pytania wykasowałem środek bo trochę tego jest)
czyli klasa robi prawie wszystko poza odpowiednim wyglądem. A teraz dziedziczenie:

  1. include_once 'abstract.menuList.php';
  2.  
  3. /**
  4.  * Klasa odpowiedzialna za wyświetlenie listy Radio na podstawie słowników
  5.  * z bazy danych
  6.  *
  7.  * @author Paweł Wołkowski
  8.  *
  9.  * @param uchwyt $con połaczenie z bazą danych
  10.  * @param string $tableName nazwa tabeli z której będzie tworzona rozwijalna lista
  11.  * @param string $fieldName wyświetlane pole w liście
  12.  * @param string $idName pole odpowiedzialne za identyfikator zrwacany z listy (id tabeli)
  13.  * @param variant $checkVal wartość domyślnego zaznaczenia (default = 0)
  14.  * @param boolean $aktywny wyświetlaj tylko aktywne wiersze (default = true)
  15.  */
  16. class radioList extends menuList {
  17. private $checkVal;
  18. private $aktywny;
  19. private $obrazki = False;
  20. private $scierzka;
  21. private $sortName;
  22.  
  23. public function __construct($tableName, $fieldName, $idName, $checkVal = 0, $aktywny = True) {
  24. parent::__construct($tableName, $fieldName, $idName);
  25. }
  26.  
  27. protected function prepear() {
  28. if($this->aktywny == TRUE) {
  29. $this->addWhereAnd("aktywny = 't'");
  30. }
  31. if($this->checkVal != 0) $this->addWhereOr($this->idName = $this->checkVal);
  32. $this->sortById();
  33. }
  34.  
  35. /**
  36.   * Funckaja ustawia parametr obrazki na true/false jeżeli w liście radio zamiast tekstu
  37.   * mają być wyświetlane obrazki
  38.   * @param boolean
  39.   */
  40. public function setObrazki($obrazki) {
  41. $this->obrazki = $obrazki;
  42. }
  43. /**
  44.   * Funckaja ustawia parametr ścierzka na string jeżeli w liście radio zamiast tekstu
  45.   * mają być wyświetlane obrazki wg podanej ścierzki
  46.   * @param string $scierzka
  47.   */
  48. public function setScierzka($scierzka = 'pic/') {
  49. $this->scierzka = $scierzka;
  50. }
  51.  
  52. public function PrintList() {
  53. $ar = $this->getItemDB();
  54. foreach ($ar as $pole) {
  55. if($this->checkVal == $pole[$this->fieldName]) {
  56. $selected = ' checked="checked" ';
  57. }else {
  58. $selected = '';
  59. }
  60. if($this->obrazki){
  61. $label = '<img src="pic/'.$pole[$this->fieldName].'.png" width="16" height="16" alt="'.$pole[$this->fieldName].'" title="'.$pole[$this->fieldName].'"/>';
  62. }else{
  63. $label = $pole[$this->fieldName];
  64. }
  65. $tekst .= '<input type="radio" '.$selected.' name="'.$this->tableName.'" value="'.$pole['id'].'" /><label for="imie">'.$label.'</label>';
  66. }
  67. return $tekst;
  68. }
  69. }
  70.  


Druga klasa

  1. include_once 'abstract.menuList.php';
  2.  
  3. /**
  4.  *
  5.  *
  6.  * @author Paweł Wołkowski
  7.  *
  8.  */
  9. final class selectMenu extends menuList {
  10.  
  11. private $aktywny;
  12. private $checkVal;
  13. private $wolne;
  14. private $firstField = '___BRAK___';
  15.  
  16. public function __construct($tableName, $fieldName, $idName, $checkVal = '', $aktywny = False, $wolne = False) {
  17. parent::__construct($tableName, $fieldName, $idName);
  18.  
  19. $this->aktywny = $aktywny;
  20. $this->checkVal = $checkVal;
  21. $this->wolne = $wolne;
  22.  
  23. $this->prepear();
  24. }
  25.  
  26. protected function prepear() {
  27.  
  28. if($this->aktywny == TRUE) {
  29. $this->addWhereAnd("aktywny = 't'");
  30. if($this->checkVal != '') {
  31. $this->addWhereOr("$this->idName = $this->checkVal");
  32. }
  33. }
  34.  
  35. if($this->wolne == TRUE) {
  36. $this->addWhereAnd("stan = 'W'");
  37. if($this->checkVal != '') {
  38. $this->addWhereOr("$this->idName = $this->checkVal");
  39. }
  40. }
  41.  
  42. $this->checkByName();
  43. $this->sortByName();
  44.  
  45. }
  46.  
  47.  
  48. public function PrintList() {
  49.  
  50. $ar = $this->getItemDB();
  51. $tekst = '<SELECT NAME="'.$this->idName.'" SIZE="1" style="width:111">'. "\n";
  52. $tekst .= '<OPTION VALUE="">'.$this->firstField.'</OPTION>'. "\n";
  53. foreach ($ar as $pole) {
  54. if($this->checkVal == $pole[$this->checkBy]) {
  55. $selected = ' SELECTED = "SELECTED" ';
  56. }else {
  57. $selected = '';
  58. }
  59. $tekst .= '<OPTION VALUE="'.$pole[$this->idName].'"'.$selected.'>'.$pole[$this->fieldName].'</OPTION>'. "\n";
  60. }
  61. $tekst .= '</SELECT>'. "\n";
  62. return $tekst;
  63. }
  64. }
  65.  
  66. $test = new selectMenu('nawigacja', 'numer_ewidencyjny', 'id_naw','30');//nawigacja nazwa tabeli, numer_ewidencyjny - wyświetlana wartość, id_naw zwracany identyfikator, 30 - zaznaczona pozycja
  67. $test->checkById();
  68. $test->sortByName('DESC');//standardowo malejąco ale jak trzeba to można rosnąco
  69. echo $test->PrintList();


Dziedziczenie jest w tym przypadku bardzo fajne bo teraz wpadłem na pomysł i chcę rozbudować te klasy o możliwość podawania tablicy z której wygenerowane zostaną słowniki a nie jak teraz tylko z bazy. Tak musiałbym przerabiać funkcje pod nowe wymagania a tak tylko przeciążenie konstruktora i pozamiatane smile.gif
thek
Powiem w miarę prosto słowami. Dziedziczenie jest używane tam, gdzie klasa która ma powstać jest bardzo podobna do już istniejącej i wykorzystuje rozwiązania, które ona implementuje. Najczęściej używa sie go gdy owa nowa klasa rozszerza możliwości klasy starszej. Wyobraź sobie sytuację, że każde dziecko jest idealną kopią swoich rodziców i dziedziczy po nich wszystko (w php nie jest to możliwe, gdyż nie można dziedziczyć po więcej niż 1 klasie, ale to "szczegół" winksmiley.jpg, przyjmij że php pozwala Ci wybrać tylko jednego rodzica do tego celu tongue.gif ). Ma więc wszystkie cechy zarówno swojej matki, jak i swojego ojca (w php wszystko albo ojca, albo matki, zależnie po kim dziedziczysz), wszystkie ich zdolności + jeszcze dodaje swoje własne. Na starcie więc nie musi się niemal niczego uczyć, gdyż posiada pełną wiedzę i umiejętności obojga (jednego z) rodziców. Wszystko co jest nowe, poszerza jego umiejętności. Podobnie się ma rzecz z programowaniem. Dziedziczenie określa pewne umiejętności podstawowe, które nowa klasa "przejmuje" po rodzicu. Co ciekawe, to może dziecko owe stare metody "poprawić". Wystarczy, że funkcje owe zostaną w nowej klasie napisane na nowo. A by było jeszcze ciekawiej, dziecko może wybrać czy chce korzystać w takim wypadku z własnej metody, czy odziedziczonej po rodzicu smile.gif

Nie myl dziedziczenia z implementacją. Dziedziczenie to bowiem "skopiowanie żywcem" od rodzica funkcji, metod. Implementacja określa tylko, że pewne rzeczy muszą się znaleźć w nowej klasie, ale nie mówi jak i co. To od Ciebie zależy co z tym zrobisz. Dlatego właśnie nawet nazewnictwo dziedziczenia łatwo pamiętać... jest to bowiem extends, a to po angielsku znaczy "rozszerz". To zaś w dużej mierze główne zastosowanie dziedziczenia: poszerzanie możliwości już istniejących klas bez konieczności kopiowania całego kodu na zasadzie, weź wszystko od mojego rodzica smile.gif
Damiankossek
No kodzik fajny i zaczeło mi już coś świtać smile.gif
Czyli w pierwszej klasie zrobiłeś sobie połączenia z bazą danych itp smile.gif
A w drugiej i trzeciej reszte winksmiley.jpg Powiedzcie mi jeszcze jak takie coś można byłoby zastosować w Kohanie ? Czy w ogóle się da używać tam dziedziczenia smile.gif ? Bo z chęcią nauczyłbym się używania tej magicznej rzeczy smile.gif

Pozdrawiam
230005
Tak jak kolega wyżej napisał, albo możesz sobie spojrzeć np. w manual php na klasy DOMNode i przyległe. Np. DOMDocument po niej dziedziczy - DOMNode zapewnia jakąś podstawową funkcjonalność wspólną dla każdego swojego potomka, a potomkowie dodają coś od siebie, bo np. nie było sensu żeby dana metoda znalazła się wyżej w hierarchii (w tym przypadku w klasie DOMNode), bo jest specyficzne tylko dla nich. W ten sposób każdy stworzony obiekt posiada tylko to co jest mu niezbędne. Dla programisty natomiast łatwiej jest zapamiętać że potrzebna mu metoda znajduje się np w klasie DOMElement, niż szukać jej w gąszczu metod DOMNode.

Oczywiście nie musisz wcale używać dziedziczenia, ale bardzo ułatwia ono ponowne użycie kodu. Na przykładzie obsługi plików:

  1.  
  2. class File {
  3. protected $_name;
  4. protected $_size;
  5. protected $_type;
  6.  
  7. public function __construct() {
  8. //tu następuje zainicjowanie pól klasy odpowiednimi wartościami
  9. }
  10.  
  11. public function create() {
  12.  
  13. }
  14.  
  15. public function remove() {
  16.  
  17. }
  18. }
  19.  


Dysponujesz klasą, która posiada podstawową funkcjonalność wspólną dla każdego pliku. Teraz powiedzmy że chciałbyś do swojego skryptu, w którym używasz tej klasy dodać pewną specyficzną funkcjonalność, np. dla obrazków i plików tekstowych. Masz dla wyjścia - albo przerobić (dodać) kod klasy file i zmienić jej nazwę (dwukrotnie), albo dziedziczyć:

  1.  
  2. class Image extends File {
  3. public function __construct() {
  4. parent::__construct();
  5. }
  6.  
  7. public function crop() {
  8. }
  9.  
  10. public function scale() {
  11. }
  12.  
  13. public function round() {
  14. }
  15. }
  16.  
  17. class Txt extends File {
  18. private $_content;
  19.  
  20. public function __construct($content) {
  21. parent::__construct();
  22. $this->_content = $content;
  23. }
  24.  
  25. public function addContent($addedContent) {
  26. //tralala
  27. }
  28. }
  29.  


Kumasz? biggrin.gif
Crozin
Cytat
Nie myl dziedziczenia z implementacją.
To tak trochę bardziej łopatologicznie:
Dziedziczenie interfejsu: interfejsy
Dziedziczenie interfejsu + implementacji: dziedziczenie

Kiedy używać dziedziczenia?
Bardzo prosto... kiedy o obiekcie klasy A możesz powiedzieć, że jest on również obiektem klasy B.
O ile dobrze pamiętam na stronach Suna fajnie było to opisane: http://java.sun.com/docs/books/tutorial/ja...nheritance.html
W ogóle dobrze będzie jak sobie przeczytasz: http://java.sun.com/docs/books/tutorial/ja...epts/index.html (co prawda w PHP nie ma paczek (ang. package), ale są przestrzenie nazw (ang. namespaces), które w pewnym sensie oznaczają to samo) - będziesz przynajmniej wiedział jakie są podstawowe narzędzia obiektówki.

Cytat
Wiem że jest to `jakby` rozdrabnianie klas
To nie jest rozdrabnianie - to jest precyzowanie/wyszegóławianie (niepoprawnie to napisałem) klas.
Cytat
Prosiłbym kogoś o takie szybkie i łopatologiczne rozpisanie jakiegoś skryptu np. newsów, artykułów czy czego kolwiek (byle nie zwierząt
Przykłady ze zwierzętami czy kształtami są bardzo dobre - ale w świecie Javy itp., a nie PHP.

No to na szybko. Czym jest news i artykuł? Oba te obiekty są formą jakiegoś wpisu, które współdzielą jakieś cechy (autor, data utworzenia, treść).
  1. <?php
  2.  
  3. abstract class TextItem {
  4. protected $author;
  5. protected $title;
  6. protected $content;
  7. protected $created_at;
  8.  
  9. protected $viewed = 0; // ilość wyświetleń
  10.  
  11. abstract function load($id); // metoda wczytująca z bazy i wypełniająca obiekt pobranymi danymi
  12.  
  13. public function increaseViewed() {
  14. ++$this->viewed;
  15. }
  16.  
  17. // tutaj cała masa getterów/setterów
  18. }
  19.  
  20. class News extends TextItem {
  21. protected $short_content; // News ma jeszcze skrócona tresc
  22. protected $display_on_abc; // News ma jeszcze właściwość "Wyświetlaj w ABC"
  23.  
  24. public function load($id) {
  25. //....
  26. }
  27.  
  28. // gettery/settery dla dwóch nowych pól
  29. }
  30.  
  31. class Article extends TextItem {
  32. // A to akurat nie ma niczego specjalnego, ale oczywiście mogło by mieć. Np.: zaimplementowany podział treści na strony
  33.  
  34. public function load($id) {
  35. //....
  36. }
  37. }


Cytat
ak dziedziczyć w Kohanie ?
Nie znam tego FW, ale... normalnie? Poprzez extends
Cytat
Bo widzę że moje wszystkie modele i kontrolery już dziedziczą klasy wyznaczone przez Kohane _Models i _Controllers ... Da się jeszcze podpiąć coś czy nie bardzo ?
Możesz sobie utworzyć swoją własną klasę np.: MyController, która (by silnik Kohany mógł to obsłużyć) musi dziedziczyć po _Controllers. Wtedy w niej możesz zaimplementować swoje własne metody, a kontrolery niech dziedziczą po Twojej klasie. De facto będą one dziedziczyć po MyController i _Controllers (czyli obu).
thek
Moja wina... Crozin zauważył moją pomyłkę smile.gif Pisałem implementacja mając na myśli interfejsy biggrin.gif

Co do Kohany to ja bardzo często dziedziczę w niej. Jak to wygląda? Ano choćby tak, że Kohana ma domyślnie swoją klasę Template jest ona w sumie uboga, ale udostępnia swoim potomkom wystarczająco by na jej podstawie sobie szablon główny strony utworzyć. Ten szablon, już dostosowany do moich potrzeb strony, jest z kolei podstawą, by ładnie rozpisać szablon osobny dla usera i osobny dla admina. Tworzy się więc zależność:
Template Kohanowski po którym dziedziczy Template Bazowy strony. A z kolei po Template Bazowy dziedziczą template Admin i Template User.
Można też zrobić dziedziczenie uprawnień choćby
Klasa obsługi gościa. Rozszerza ją klasa Registered User bo ma to samo co Gość + kilka funkcji dodatkowych. Moderator dziedziczy po Userze rozszerzając o kolejne funkcje. Zaś Admin dziedziczy i rozszerza Moderatora smile.gif Zauważ, że w razie czego nowe funkcjonalności "przechodzą" na klasy pochodne. Jeśli dodam coś do Gość to będą mieć to wszyscy. Jeśli dodam do Moderator, to i Admin też to otrzyma, ale już Gość i User nie, bo są "starsze". Tworzy się drzewko dziedziczenia.
batman
~Damiankossek
Praktyczne zastosowanie już znasz, teraz kolej na teoretyczny bełkot winksmiley.jpg
Dziedziczenie, w zależności od strony z której spojrzeć, nazywa się specjalizacją lub generalizacją. O specjalizacji mówimy gdy patrzymy na klasy "od góry", czyli od pierwszej/głównej klasy. Każda klasa, która dziedziczy po pierwszej jest specjalną odmianą rodzica. Klasa taka posiada wszystkie elementy rodzica oraz własne, specjalne, elementy. Stąd nazwa - specjalizacja. Oczywiście łańcuch dziedziczenia nie kończy się na jednej klasie. Wyspecjalizowanych klas może być więcej, np Klasa1 -> Klasa11 -> Klasa111...
Generalizacja jest odwrotnością specjalizacji i polega na patrzeniu na hierarchię klas "od dołu". Czyli zaczynamy od klasy, po której nic już nie dziedziczy i przesuwamy się "do góry". W takim przypadku rodzic jest uogólnionym przypadkiem danej klasy. Innymi słowy jest to to samo, tylko ma mniejsze możliwości.

Jeśli chcesz poznać więcej teorii, to poczytaj na temat diagramów klas w UML.
vegelus
W "Kohanej" można stosować własne klasy w formie bibliotek więc jak najbardziej nawet mój przykład tam pasuje i wykorzystuję go na mojej stronie.
Ja podchodzę do dziedziczenia tak.

Mam do napisania kilka klas robiących coś.
Jeżeli 20% lub więcej kodu powtarza się w każdej klasie to znaczy, że trzeba zastosować dziedziczenie. I wywalić powtarzający się kod do rodzica (często posiadającego metody abstrakcyjne aby zachować interfejs) i potem sobie dziedziczę.
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-2024 Invision Power Services, Inc.