Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Sens
Forum PHP.pl > Forum > PHP > Object-oriented programming
IAmBoskiM
Witam.

Nie rozumiem paru rzeczy w programowaniu obiektowym, mianowicie sensu interfejsów i abstrakcji. Przecież taki interfejs w ogóle jest niepotrzebny i tylko zwiększa (nieznacznie) rozmiar naszego pliku, albowiem on tylko wymusza zdefiniowanie jakiejś metody. Ogólnie tą metodę możemy zdefiniować bez żadnego interfejsu - efekt ten sam.

  1. <?php
  2. //******************** kod 1
  3. interface intf {
  4. public function example();
  5. }
  6. class klasa implements intf {
  7. public function example() {
  8. echo 'Example function';
  9. }
  10. }
  11. //********************
  12. //******************** kod 2
  13. class klasa {
  14. public function example() {
  15. echo 'Example function';
  16. }
  17. }
  18. //********************
  19. // Efekt? Ten sam, a zatem po co te interfejsy??
  20. ?>


Abstrakcji też nie rozumiem. Przecież ów deklaracja jest absolutnie niepotrzebna; można to zrobić w klasie, która coś dziedziczy.

  1. <?php
  2. //*************** kod 1
  3. abstract class abstr {
  4. abstract public function absfunc ();
  5. public function example () {
  6. echo 'example';
  7. }
  8. }
  9. class klasa extends abstr {
  10. public function absfunc () {
  11. echo 'abstr';
  12. }
  13. }
  14. //***************
  15. //*************** kod 2
  16. class notabstr {
  17. public function example () {
  18. echo 'example';
  19. }
  20. }
  21. class klasa extends notabstr {
  22. public function absfunc () {
  23. echo 'abstr';
  24. }
  25. }
  26. //***************
  27. // Efekt? Ten sam...
  28. ?>


Ogólnie nie widzę sensu w użytku tego - jest to używane tylko do debugowania albo przejrzystości czy jak? Bo ogółem nie ma to innego sensu raczej.
viking
Po pierwsze stosuje się PSR-4 http://www.php-fig.org/psr/psr-4/ i interface, trait oraz wszystkie klasy powinny być definiowane w osobnych plikach.
Po drugie implementacja interface w klasie wymusza własną implementację danej metody czyli w twoim pierwszym przykładzie intf::example(). Strukturyzuje też kod.
Załóżmy gdybyś miał:

UserStorageInterface.php
  1. interface UserStorageInterface {
  2. public function fetchById($id);
  3. public function fetchAll();
  4. }


DbUserStorage.php
  1. class DbUserStorage implements UserStorageInterface {
  2. }


ArrayUserStorage.php
  1. class ArrayUserStorage implements UserStorageInterface {
  2. }


ExternalServiceUserStorage.php - czyli dane pochodzące z obcych serwisów pobierane przez API
  1. class ExternalServiceUserStorage implements UserStorageInterface {
  2. }


W każdej z klas musiałbyś zaimplementować pobieranie danych o użytkownikach. Metoda na zrobienie tego byłaby różna w zależności od klasy (wybranie z bazy, pobranie z tablicy albo z zewnątrz) ale miałbyś pewność że klasy te posiadają daną metodę. I teraz jeszcze ważniejsze:

  1. $dbUserStorage = new DbUserStorage;
  2. var_dump($dbUserStorage instanceof UserStorageInterface);
  3. // bool(true)


Oraz co za tym idzie rzutowanie:

UserRepository.php
  1. class UserRepository {
  2. /**
  3.   * @var UserStorageInterface
  4.   */
  5. private $userStorage;
  6.  
  7. /**
  8.   * UserRepository constructor.
  9.   *
  10.   * @param UserStorageInterface $userStorage
  11.   */
  12. public function __construct(UserStorageInterface $userStorage)
  13. {
  14. $this->userStorage = $userStorage;
  15. }
  16. }


Dzięki temu do klasy UserRepository możesz przekazać dowolny obiekt który implementuje UserStorageInterface.

Jakiś czas temu pisałem też o klasach gdzie masz wytłumaczoną abstrakcję.
LowiczakPL
Na początku programowania OO też nie rozumiałem w ogóle co to interface i abstract class.

Aktualnie rozumiem to w ten sposób że:

Interfejs i Klasa Abstrakcyjna jest to narzucony przez programistę sposób nazewnictwa metod, taka instrukcja dla innych programistów, lub dla siebie samego.

Interfejs zawiera jedynie deklarację metod, klasa abstrakcyjna może zawierać dodatkowo działające abstrakcyjne metody.

Pisząc swoje proste aplikacje/frameworki nie stosowałem nigdy interfejsów, natomiast bardzo często pisałem klasy abstrakcyjne, może za często.
phpion
Z tymi "działającymi abstrakcyjnymi metodami" to się chyba trochę zagalopowałeś. W skrócie interfejs tym różni się od klasy abstrakcyjnej, że definiuje co robi dana klasa, a nie jak.
Lion
Interfejs jest po to abyś mógł podpowiedzieć sobie lub innemu programiście czego oczekujesz od danego obiektu klasy, co powinien on umieć zrobić. Gdy np. przyjmujesz obiekt poprzez dependency injection, jak zostało to pokazane wyżej w UserRepository, to możesz się spodziewać że przekazany Ci obiekt będzie robił to co interfejs UserStorageInterface mu każe. Oznacza to że spokojnie w innym miejscu możesz wywoływać na danym obiekcie metody zaimplementowane z interfejsu UserStorageInterface bez obaw że ich tam nie ma i że dostaniesz fatal error podczas wykonywania kodu. Gdy natomiast jesteś zainteresowany tym by skorzystać z UserRepository to od razu wiesz jakie metody powinieneś zaimplementować w swoich obiektach.
com
Interfejsy są rozwiązaniem wielodziedziczenia, które znane jest z c++, wiec jak potrzebujesz przykładowo 2 klasy które będą miały takie same metody, ale ich działanie będzie inne to najłatwiej zrobić dla nich interfejs i wtedy masz uporządkowana strukturę, a klasa abstrakcyjna to częściowa cześć wspólna dlatego nie da sie stworzyć obiektu takiej klasy. W php mamy jeszcze cechy wink.gif
Pyton_000
@com to co napisałeś to nie jest wielodziedziczenie smile.gif Wielodziedziczenie jest wtedy gdy klasa dziedziczy po kilku klasach. Namiastką tego są Traits.

Przykład z moich ostatnich wojów. Mam sobie Commands (kilka różnych). Tworzę sobie rozszerzoną wersję każdego. Siłą rzeczy robię extends dla każdego z odpowiedniego Commands.
Jednak moje commands mają wspólne metody. I tu by się przydało wielodziedziczenie żeby wypchnąć te metody do innego wspólnego Command, ale nie da się. I tu wchodzi Traits.
IAmBoskiM
Traitsy jak najbardziej mają sens, zmniejszają rozmiar pliku + nie musisz kopiować tych samych rzeczy do różnych klas. Po prostu mniej pracy.

Hmm, no cóż, może kiedyś postanowię jednak używać te interfejsy i abstrakcje. Przynajmniej ujrzałem jakieś możliwe zastosowanie, dziękuję.

A tak poza tym czemu trzeba dołączać wszystkie klasy w oddzielnych plikach? Osobiście uważam, że wolę mieć deklaracje klas w innym pliku niż ten, w którym będą mi one potrzebne - mniej scrollowania itp., ale czemu każda klasa w oddzielnym pliku? Ma to w ogóle jakiekolwiek znaczenie?
NickOver
Cytat(IAmBoskiM @ 15.07.2016, 21:39:56 ) *
Traitsy jak najbardziej mają sens, zmniejszają rozmiar pliku + nie musisz kopiować tych samych rzeczy do różnych klas. Po prostu mniej pracy.

Hmm, no cóż, może kiedyś postanowię jednak używać te interfejsy i abstrakcje. Przynajmniej ujrzałem jakieś możliwe zastosowanie, dziękuję.

A tak poza tym czemu trzeba dołączać wszystkie klasy w oddzielnych plikach? Osobiście uważam, że wolę mieć deklaracje klas w innym pliku niż ten, w którym będą mi one potrzebne - mniej scrollowania itp., ale czemu każda klasa w oddzielnym pliku? Ma to w ogóle jakiekolwiek znaczenie?


Nie trzeba ale ułatwia to czytanie kodu.
Łatwiej jest szukać metody getUserId z klasu Users która jest w pliku users.class.php niż w pliku allClasses.php
viking
Robi się tak ze względu na autoloader który automatycznie mapuje nazwę pliku na nazwę klasy. I jest dużo bardziej czytelne.
A juz na pewno nie używa się teraz.class.php. to było za czasow początków PHP 5.
com
Pyton_000 ja wiem co to wielodziedziczenie, może źle się wyraziłem, w cpp było wielodziedziczenie, którego ludzie strasznie nie lubili w takiej postaci w jakiej było, stad wzrosła popularność interfejsów i klas abstrakcyjnych, które pozwalały w takich językach jak np java stworzyć namiastkę tego co było w cpp ale innym sposobem.

No ale jak masz klasę Foo, która będzie abstrakcyjna i cześć wspólna zaimplementujesz a resztę pozostawisz ew nadpiszesz w pochodnej, to da się nie używać traintów biggrin.gif

przykład https://github.com/zendframework/zend-diactoros to dało sie napisac znacznie prościej bez traitów, które bardzo często zaciemniają tylko kod wink.gif
kkarpieszuk
Hej @IAmBoskiM widze, że jestes w dość podobnej sytuacji co ja smile.gif Tyle, że ja już zrozumiałem co to są interfejsy/abstrakcje i po co smile.gif

Generalnie masz bardzo dużo racji: one są niemal niepotrzebne w starszych wersjach PHP. W najnowszej 7.0 już jest lepiej ale nadal niedoskonale. Ich znaczenie zrozumiałem dopiero gdy poczytałem kilka książek o programowaniu gdzie opisane są jezyki naprawdę obiektowe


W dobrym języku obiektowym deklarujac jakas funkcje lub metode mozna wymusic by podany argument byl danego typu: byl daną klasą lub implementowal dany interfejs lub rozszerzal daną klase abstrakcyjną.

Spojrz na ten kod:

  1. class pan_domu {
  2. public function nakarm_zwierze_miesozerne($zwierze) {
  3. $jedzenie = new Mieso();
  4.  
  5. $zwierze->je($jedzenie);
  6. }
  7.  
  8. public function nakarm_zwierze_roslinozerne($zwierze) {
  9. $jedzenie = new Salata();
  10.  
  11. $zwierze->je($jedzenie);
  12. }
  13. }


Wiec jest sobie jakis czlowiek, pan domu, ktory moze karmic swoje zwierzeta.

I teraz kod ktory uzyjemy do karmienia zwierzecia (kota):

  1. $zwierze = new Kot();
  2.  
  3. $pan = new pan_domu();
  4.  
  5. $pan_domu->nakarm_zwierze_miesozerne($zwierze);


Wszystko jest pieknie, ale nie masz zadnej kontroli nad tym czy zwierze, ktore probujesz nakarmic je mięso.

OK, wlasnie sobie myslisz: jak to nie maz? Przeciez widze wyraznie co napisalem. Utworzylem kota z jakiejs tam klasy i go karmie miesem.

To jest zupelna prawda pod warunkiem, ze to ty jestes autorem klasy Kot(), ktorej specjalnie tu nie napisalem i wewntarz tego kota sa napisales metode je(), ktora wywoluje metode gryzie(), potem trawi() i dokladniej trawi_mieso() (zakladamy ze zwierz. roslinozerne beda mialy zamiast tego trawi_rosliny()) i tak dalej.

Jednak w warunkach naturalnych nad kodem pracuje sie w wiele osob. To ktos inny dostarczy klase Kot. Mozesz oczywiscie przejrzec jak ta klasa w srodku wyglada i jesli cos jest nie tak odeslac ten kod do wspolpracownika by go poprawil. Ale jesli pracujecie nad funkcją obslugującą Zoo zajmie ci to wieki zanim przejrzysz tak wszystkie zwierzeta, prawda?

I teraz pojawia sie polsrodek w postaci interfejsu. Tworzysz interfejs Zwierzeta_Miesozerne w ktorym definiujesz co taki zwierzak musi umiec:
  1. interface Zwierzeta_Miesozerne {
  2. public function je();
  3. private function gryzie();
  4. private function trawi();
  5. private function trawi_mieso();
  6. }


I pieknie. teraz tylko mowisz wszystkim wspolracownikom, twórcom zwierząt by ich zwierzeta implementowaly ten interfejs.

Zaczynasz juz lapac? Czekaj, to jeszcze nie wszystko smile.gif

oczywiscie nadal musisz przegladac dostarczony kod czy on implementuje ten interfejs, prawda? O wiele mniej roboty, ale nadal jest robota.

Tu z pomocą przychodzi ostatnia wlasciwosc takich kontrukcji: wymuszanie typu parametrow. Przepisz swoj kod z samego poczatku mojego wyjasnienia tak by linijka:

  1. public function nakarm_zwierze_miesozerne($zwierze) {


wygladala:

  1. public function nakarm_zwierze_miesozerne( Zwierzeta_Miesozerne $zwierze) {


Takie cos wymusza by przekazane zwierze implementowalo dany interfejs. Jesli tak nie bedzie, PHP zwroci blad typu i po prostu calosc nie bedzie dzialac. Zatem juz nie musisz kodu przegladac: jesli programista cie nie poslucha, kod zwyczajnie nie bedzie dzialac.

Podsumowujac: tak jak ci to wyzej napisano, interfejsy i abstrakcje to jest rodzaj "umowy" w kodzie, umowy miedzy programistami tworzącymi różne fragmenty kodu, umowy dzieki ktorej zaden programista nie musi zagladac do srodka kodu stworzonego przez innego programiste.
com
kkarpieszuk wszystko fajnie, ale uargumentuj co masz na myśli mówiąc, że "one są niemal niepotrzebne w starszych wersjach PHP". Sorry ale co takiego 7 zmieniła w stosunku do 5?

Ok możemy zwracać konkretny typ i dodali obsługę skalarów, ale to niczego tutaj nie zmieniło.
kkarpieszuk
@com w trakcie pisania doszedlem do tego samego, ale juz nie wykasowalem smile.gif

w 5 brakuje mi jeszcze deklaracji zwracanych danych, ale ja juz siedze na 7 wiec problemow nie mam wink.gif
gitbejbe
Do autora tematu

Pytasz o znaczenie rzeczy, na temat których powstalo wiele ksiazek. Nie oczekuj ze ktokolwiek tutaj wyjaśni Ci sens. Zamiast ciągnąc temat, polecam Ci książke PHP Objects, Patterns and Practice edycje 3. Jak ja przeczytasz to zrozumiesz ze na ten moment nie masz bladego pojecia o programowaniu obiektowym. Zrozumiesz ze oop to prawdziwa sztuka, a nie tępe napisanie kilku klas - co kazdy potrafi. Pózniej wrocisz pamiecia do tego postu i sam z siebie bedziesz sie smial.

Jesli ludzie kierowali by sie zasadą ,,po co'' to do niczego bysmy nie doszli. Jesli nadal bedziesz szedl na latwizne to nigdy nie bedziesz dobrym programista - w sumie z korzyscia dla mnie bo im mniej konkurencji tym ja wiecej zarobie ; )
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.