Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: O abstrakcji klas i interfejsów
Forum PHP.pl > Forum > PHP > Object-oriented programming
daniel1302
Witam
dziś szukając informacji o abstrakcji obiektów natchnołem się na ten artykuł
http://blog.dywicki.pl/2007/04/04/o-abstra...-i-interfejsow/


Zrozumiałem tyle:
Abstrakcja
-Wymusza tworzenie obiektów w klasach pochodnych
-Przekazuje informacje o działającym skrypcie


Interfejs
-Wymusza użycie metod w klasach impletujących go
-Przekazuje nadzędzia działającego systemu


Używamy
Klasę abstrakcyjną gdy:
-Mamy mniejszy system
-Musimy zawrzeć w funkcji która będzie wymuszona jakiś kod
-Chcemy używać niektórych z jej metod


Interfejs gdy:
-Mamy wielki system
-Wymuszamy użycie metod w których nie musimy zawierać kodu
-Definiujemy medtody które zainicjujemy w klasach impletujących interfejs



Nie zrozumiałem:
-Co to jest Elastyczność obiektu
-"Budowa złożonej aplikacji bez użycia interfejsów jest posunięciem wysoce nierozsądnym, ponieważ to właśnie dzięki nim, nawet w językach kompilowanych ze statycznym typowaniem jesteśmy w stanie zyskać nadzwyczajnie elastyczny kod"
-"Nawet jeśli koncepcja implementacji obróci się o 180 stopni to nasz interfejs, będący swoistą fasadą, ukryje te zmiany przed osobami, których one nie dotyczą bądź nie interesują."


Dziękuje z góry oraz proszę o wyrozumiałośc ale jeśli mi ktoś nie wytłumaczy nie pojmę niczego tego do końca.
Cysiaczek
Chodzi o to, aby zmiana implementacji jakiejś metody nie pociągała za sobą konieczności zmian w wielu miejscach, albo co gorsza, zmian w kodzie użytkującym tą metodę. np. Dwie klasy News (ahh, nieśmiertelny przykład) - ten sam interfejs, ale działanie kodu wewnątrz klas jest inne smile.gif
  1. <?php
  2. //print date('Y-m-d', 1218467120);
  3. class Comment
  4. {
  5. protected $content;
  6. protected $id;
  7.  
  8. public function __construct($content='')
  9. {
  10. $this->content=$content;
  11. }
  12.  
  13. public function getId()
  14. {
  15. if(!$this->id)
  16. {
  17. $this->id=rand(1, 300); // ot, symulacja
  18. }
  19. return $this->id;
  20. }
  21.  
  22. public function getContent()
  23. {
  24. return $this->content;
  25. }
  26. }
  27.  
  28. interface Commentable
  29. {
  30. public function getComments();
  31. public function getComment($id);
  32. public function addComment(Comment $comment);
  33.  
  34. }
  35.  
  36. class News implements Commentable
  37. {
  38. protected $comments=array();
  39.  
  40. public function getComment($id)
  41. {
  42. return $this->comments[$id];
  43. }
  44.  
  45. public function getComments()
  46. {
  47. return $this->comments;
  48. }
  49.  
  50. public function addComment(Comment $comment)
  51. {
  52. $this->comments[$comment->getId()]=$comment;
  53. }
  54. }
  55.  
  56.  
  57. class News implements Commentable
  58. {
  59. protected $comments=array();
  60.  
  61. public function getComment($id)
  62. {
  63. if(isset($this->comments[$id]))
  64. {
  65. return $this->comments[$id];
  66. }
  67. else
  68. {
  69. throw new Exception("Cannot find comment (id: $id)");
  70. }
  71. }
  72.  
  73. public function getComments()
  74. {
  75. return $this->getFiltered($this->comments);
  76. }
  77.  
  78. public function getFiltered($comments)
  79. {
  80. $data=array();
  81. foreach($comments as $key=>$comment)
  82. {
  83. if((integer)$key%2==0)
  84. {
  85. print $key;
  86. $data[$key]=$comment;
  87. }
  88. }
  89. return $data; // tylko parzyste identyfikatory
  90. }
  91.  
  92. public function addComment(Comment $comment)
  93. {
  94. $this->comments[$comment->getId()]=$comment;
  95. }
  96. }
  97.  
  98. $news=new News();
  99.  
  100. try
  101. {
  102. $news->addComment(new Comment());
  103. $news->addComment(new Comment());
  104. $news->addComment(new Comment());
  105. print_r($news->getComments());
  106. }
  107. catch(Exception $e)
  108. {
  109. print $e->getMessage();
  110. }
  111.  
  112. ?>


Metoda News::getComment(), pobiera identyfikator jako parametr i wyszukuje odpowiadający mu obiekt w lokalnej kolekcji. Możemy też rozbudować funkcję tak, aby w przypadku nieznalezienia obiektu, szukała w bazie danych, a dopiero potem zwracała błąd. Zauważ, że zawsze przekazujemy tylko identyfikator - nie musimy nic zmieniać w wywołaniu tej metody.
Teraz mając system, który ma np. 10 klas News (np dla Swiat, Polska, Warszawa), metoda wyszukiwania i pobierania Komentarzy może być różna dla każdej z nich. Jeszcze mały przykład biggrin.gif
  1. <?php
  2. class ChinaNews extends News
  3. {
  4. public function addComment(Comment $comment)
  5. {
  6. $this->comments[$comment->getId()]=$this->censorship($comment);
  7. }
  8.  
  9. public function censorship(Comment $comment)
  10. {
  11. if(eregi("Tibet", $comment->getContent()))
  12. {
  13. throw new Exception('We apologize, but our servers are overloaded. Please try again later'); // tongue.gif
  14. }
  15. return $comment;
  16. }
  17. }
  18.  
  19. // i jeszcze kod użytkujący
  20. $news=new ChinaNews();
  21.  
  22. try
  23. {
  24. $news->addComment(new Comment('Free Tibet!!!'));
  25. }
  26. catch(Exception $e)
  27. {
  28. print $e->getMessage();
  29. }
  30. ?>

To jest właśnie elastyczność - ukrycie implementacji za interfejsem.

Pozdrawiam.

Przenoszę na OOP
daniel1302
Czyli elastyczność to zastosowanie interfejsów/ klas abstrakcyjnych które wymuszą użycie jednej metody która ma kilka zadań w zależności od klas?
Cysiaczek
Nie do końca. W pewnym stopniu tak, ale tu chodzi o ukrycie szczegółów implementacji, a metoda powinna robić zazwyczaj to samo, albo przynajmniej zwracać oczekiwane wyniki. Czyli kogoś, kto używa tej metody nie obchodzi jak ona działa. "Ten kto używa", to inny kod (tzw. użytkownik), albo inny programista.
To tak jak z samochodem - przekręcasz kierownicę w prawo i nie interesuje Cię w tym momencie, co się dzieje wewnątrz tego mechanizmu - interesuje Cię tylko to, że samochód skręca - bo taka była Twoja intencja, gdy przekręcałeś kierownicę smile.gif

Pozdrawiam.
daniel1302
Czyli Elastyczność to jest:
Ktośużywa funkcji (skrypt banu) z intencją zbanowania ale niewie jak to działa w środku skryptu wie co to robi lecz nie wie jak to jest napisane, na jakich tabelach/klasach operuje?

Tutaj wyczytałem
http://forum.php.pl/index.php?showtopic=65622&st=10

że interfejsy są bardziej potrzebne od strony dokumentacji kodu??
Cysiaczek
To jest interfejs i wtedy masz rację. Zapewnia on elastyczność, ale jest ona widoczna dopiero z punktu widzenia.. z wewnątrz metody. Programista może napisać np. 10 wersji tej samej metody i zmieniać je jak rękawiczki (np. testować najbardziej wydajną). Nie musi jednak zmieniać sposobu, w jaki ktoś tej metody używa (interfejsu). Jeśli metoda jest w systemie często wykorzystywana (bywa i kilkaset razy), to zmiana jej działania wpływa od razu na cały system, bo ten operuje na wyższym poziomie - na interfejsie właśnie. Nie musisz latać po kodzie i zmieniać jakichś zmiennych, kopiować i wklejać fragmentów kodu itp.
daniel1302
  1. <?php
  2. Interface ValidatorInterface
  3. {
  4. /**
  5. *validate function
  6. *Sprawdzaj
  7. *@var  (*)string  Tekst do sprawdzenia
  8. *@acces public
  9. */
  10. public function validate($string);
  11.  
  12. /**
  13. *getErrors function
  14. *
  15. *Zwróć blędy
  16. *@acces public
  17. */
  18. public function getErrors();
  19. }
  20.  
  21. Abstract Class Validator
  22. {
  23. protected $errorsAmount = 0;
  24. protected $validatingText;
  25. abstract public function validate($string);
  26. function TestWielopoziomowegoDziedziczenia()
  27. {
  28. echo 'Testuje';
  29. }
  30. }
  31.  
  32. Class StringValidator extends Validator
  33. {
  34. public function validate($string)
  35. {
  36. $this->validatingText = $string;
  37. }
  38.  
  39.  
  40. }
  41.  
  42. Class IntValidator extends Validator
  43. {
  44. public function validate($int)
  45. {
  46. $this->validatingText = $int;
  47. }
  48. }
  49.  
  50. Class EmailValidator extends StringValidator implements ValidatorInterface
  51. {
  52. #Algorytm poprawności
  53. protected $pattern = '/^[a-zA-Z0-9-.]+@[a-z0-9-]+.[a-z0-9-]{2,6}+$/';
  54.  
  55.  
  56. /**
  57. *Trwa sprawdzanie
  58. *
  59. *@return bool Poprawność
  60. */
  61. public function validate($subject)
  62. {
  63. #Sprawdz
  64. if (strlen($subject) < 5)
  65. ++$this->errorsAmount;
  66. if (!preg_match($this->pattern, $subject))
  67. ++$this->errorsAmount;
  68.  
  69. $arrSubjectPart = explode('@', $subject);
  70. if (count($arrSubjectPart) != 2)
  71. ++$this->errorsAmount;
  72.  
  73. #Zwróć wynik
  74. return ($this->errorsAmount > 0) ? false : true;
  75. }
  76.  
  77.  
  78. public function getErrors()
  79. {
  80. return $this->errorsAmountAmount;
  81. }
  82.  
  83. /**
  84. *Usuń niepotrzebne dane
  85. *
  86. *@return null null
  87. */
  88. function __destruct()
  89. {
  90. unset($this->pattern);
  91. unset($this->validatingText);
  92. unset($this->errorsAmountAmount);
  93. }
  94. }
  95.  
  96. Class NumValidator extends IntValidator implements ValidatorInterface
  97. {
  98. public function validate($int)
  99. {
  100. if (!is_int($int))
  101. ++$this->errorsAmount;
  102. else
  103. return true;
  104. }
  105. public function getErrors()
  106. {
  107. return $this->errorsAmountAmount;
  108. }
  109. }
  110.  
  111. $numValidator = new NumValidator;
  112. $numValidator->TestWielopoziomowegoDziedziczenia();
  113. ?>


Tutaj znalazłem zastosowanie interfejsów
bo klasa abstrakcyjna nie wymusi metody u klasy wnuka

A elastyczności nie mogę zrozumieć. Ok więc robie tak:
Piszę
klasy

  1. <?php
  2. Interface Układy
  3. {
  4. public function kierownicy();
  5. public function silnika();
  6. public function napędu();
  7. public function biegów();
  8. }
  9.  
  10.  
  11. Abstract Class Samochod
  12. {
  13. protected $rok;
  14. protected $stan;
  15. protected $marka;
  16. protected $model;
  17.  
  18. public function zwrocRok()
  19. {
  20. return $this->rok;
  21. }
  22.  
  23. public function zwrocStan()
  24. {
  25. return $this->stan;
  26. }
  27.  
  28. public function zwrocModel()
  29. {
  30. return $this->model;
  31. }
  32.  
  33. public function zwrocMarka()
  34. {
  35. return $this->marka;
  36. }
  37. }
  38. ?>


I dam tą klase komuś
i w zależności czy ktoś dopisze funkcje
  1. <?php
  2. function unapedu()
  3. {
  4. return ++$gaz;
  5. }
  6. ?>


czy

  1. <?php
  2. function unapedu()
  3. {
  4. return $gaz += 2*$gaz;
  5. }
  6. ?>



To zawsze doda gazu.

Czy to jest elastyczność

Z pewnego wątku na forum mam takie coś
  1. <?php
  2. interface inteligencjaNaPewenymPoziomie{}
  3. abstract class Ssaki{}
  4.  
  5. class Czlowiek extends Ssaki implements inteligencjaNaPewenymPoziomie{}
  6. class Orangutan extends Ssaki{}
  7.  
  8. // 2 podobne, ale jakże różne klasy Budowniczy
  9.  
  10. class Budowniczy{
  11.  
  12. function budujSamolot(Ssaki $object){}
  13. function budujSamolot(Ssaki $object); //orangutan z powodzeniem wybuduje na samolot : )
  14. function malujWPaincie(Ssaki $object){} // orangutan i czlowiek mogą używać Painta(TM)
  15. }
  16.  
  17.  
  18. class Budowniczy{
  19.  
  20. function __construct(Ssaki $object){}
  21. function budujSamolot(inteligencjaNaPewenymPoziomie $object){} // tutaj tylko obiekt typu inteligencjaNaPewenymPoziomie bedzie mógł wykonać zadan
    ie
  22. function malujWPaincie(Ssaki $object){} // orangutan zawsze może pomalować : P
  23. }
  24. ?>


Chodzi mi na temat o info o czymś takim
budujSamolot(inteligencjaNaPewenymPoziomie $object)

albo widzialem gdzieś takie coś funkcja(JakasKlasa $object)?
Crozin
  1. <?
  2. metoda_czy_funkcja(NAZWA $zmienna){
  3. }
  4. ?>
W PHP taka konstrukcja oznacza, że parametr $zmienna musi:
1) Obiektem klasy NAZWA
2) Obiektem, któego klasa dziedziczy po NAZWA
3) Obiektem, którego klasa implementuje interfejs NAZWA
(dodatkowo takie wymusznie typu możesz zostosować jeszcze do tablic (array). I niestety tylko tyle)


Co do interefejsów:
Wyobraź sobie, że masz obok siebie cztery telewizory. Każdy z nich ma pięć przycisków:
1) Power (off/on)
2) Zwiększ głośność
3) Zmniejsz głośność
4) Kanał w górę
5) Kanał w dół

Gdy chcesz włączyć którykolwiek z tych telewizorów wykonujesz następującą akację: wciskasz przycisk power. I z Twojej strony to już jest koniec operacji. Teraz wewnątrz telewizora jego elektornika włącza wyświetlacz (zwróć uwagę, że Ciebie to już nie interesuje czy to jest kineskop, ekran LCD czy plazma), głośniki (ponownie nie interesuje Cie czy to jest jeden głośnik mono czy stereo czy może telewizor korzysta z zewnętrznego układu głośników) itp.

Jeszcze jeden przykład: Jakiego byś telewizoru nie kupił będziesz potrafił korzystać z jego podstawowych opcji na pilocie (5 punktów powyżej). Dlaczego? Ponieważ jest tam powtórzony pewien schemat, jednolity interfejs. Oczywiście pilot może oferować dużo większe możliwości (takie, których inne nie oferują) ale te, które są zapisane w interfejscie wykonuje dokładnie tak samo jak inne piloty.

A więc interfejs ujednolica dostęp do określonych zachowań (w OOP można by przyjąć, że w polach przechowujemy stany obiektu (np. telewizor: włączony/wyłączony, glośność 80, 70, 100, kontrast: 75, 22) a w metodach mamy "zachowania" (zmiejsz/zwiększ kontrast, włącz/wyłącz))

Co do klas/metod abstrakcyjnych. One pozwalają na to by pewne zachowania były wspólne dla różnych obiektów. Dzięki temu, że raz zapiszemy pewne zachowanie w klasie abstrakcyjnej (którą dziedziczy klasa dziecko) nie musimy pisać tego samego kodu 5 razy. Ponownie przykład telewizorów:
  1. <?
  2.  
  3. interface iTelewizor{
  4. public function wlacz();
  5. public function glosnosc($wartosc = 0);
  6. public function kontrast($wartosc = 100);
  7. }
  8.  
  9. abstract class Telewizor{
  10. protected $wlaczony = false;
  11. protected $glosnosc = 75;
  12. protected $mute = false;
  13. protected $kontrast = 40;
  14.  
  15. //wszystkie telewizory dzialaja tak samo. Jak jest wlaczony i klikniemy "Power" to sie wylaczy i vice versa
  16. public function wlacz(){
  17. $this->wlaczony = ($this->wlaczony === false) ? true : false;
  18. }
  19.  
  20. protected function mute(){
  21. return $this->mute = ($this->mute === false) ? true : false;
  22. }
  23. }
  24.  
  25. class Kineskopowy extends Telewizor implements iTelewizor{
  26. //nasz kineskopowy telewizor w przypadku gdy glosnosc jest rowna zeru wlacza funkc
    je mute.
  27. public function glosnosc($wartosc = 0){
  28. if($wartosc == 0)
  29. return $this->mute();
  30.  
  31. $this->glosnosc = (int) $wartosc;
  32. }
  33.  
  34. //reszta klasy bez metody wlacz();
  35. }
  36.  
  37. class LCD extends Telewizor implements iTelewizor{
  38. //natomiast nasz nowy LCD, dbajac o nasze zdrowie w przypadku proby ustawienia zby
    t duzej glosnosci poprosi jeszcze o potwierdzenie
  39. //przy $wartosc = 0, nie wlacza mute, poprostu scisza do zera
  40. public function glosnosc($wartosc = 0){
  41. if($wartosc >= 75){
  42. $this->potwierdzenieGlosnosci();
  43. }
  44.  
  45. $this->glosnosc = (int) $wartosc;
  46.  
  47. if($this->posiadamZewnetrzySystemGlosnikow()){
  48. $this->glosnoscSubbuffera = $this->zmiejszGlosnoscSubbufera($wartosc); //w osobnej metodzie, ktora ma jakis tam algorytm okreslajacy o ile zmiejszyc (normalnie powinno okreslac czy zmiejszyc czy wiekszyc w zaleznosci od tego 
    co robimy) glosnosc glosnika niskotonowego
  49. }
  50. }
  51.  
  52. //reszta klasy bez metody wlacz();
  53. }
  54. ?>


Klasy abstrakcyjne pozwalają nam (programiście, nie uzytkownikowi (warto zaznaczyć, że programista jest czasami użytkownikiem)) zaimplementować pewne powatarzale zachowania.
daniel1302
Dziękuje rozjaśniło mi się Pozdrowienia dla dwóch osób które zechciały pomóc
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.