Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Interfejsy
Forum PHP.pl > Forum > PHP > Object-oriented programming
Stron: 1, 2
sztosz
Mam jedno pytanie: Do czego tak na prawdę mogą się przydać interfejsy w programowaniu? Bo za cholerę nie wiem. Szukam unikalnego zastosowania, którego nie dało by się z powodzeniem zastąpić czymś innym i nie znajduję. Do czego to więc?
Speedy
Jeśli chodzi o interfejsy w php, to mogą się one przydać np. do budowania prototypu dla jakiegoś zestawu klas, do wymuszania stworzenia konkretnej metody (lub metod) w różnych klasach, do ujednolicenia lub usystematyzowania wybranego zestawu klas. Implementując interfejs nie zapomnimy o żadnej potrzebnej metodzie. Czasem zdarza się, że jakaś aplikacja ma wiele kontrolerów w postaci klas, które mają bardzo podobną budowę tylko pobierają różne dane i na różny sposób je prezentują. Te kontrolery muszą mieć konkretne metody o konkretnych nazwach, które powielają się każdym z nich, ale zwracają co innego. Za pomocą interfejsu możemy automatycznie zapewnić spójność tych kontrolerów i zapewnić uruchomienie każdego kontrolera tylko w przypadku, gdy posiada wszystkie potrzebne metody.
wookieb
Przydają się np do tego jak tworzysz klasy elementów formularza. Chcesz, żeby każda klasa elementu musiała zdefiniować pewne metody, właściwości np getHtml, isValid itd.
Dzięki temu masz pewność, że klasy które implementują ten interfejs, będą posiadały podane metody.

Różnica pomiędzy interfejsem a klasą abstrakcyjną to np taka, że możesz implementować wiele interfejsów a dziedziczyć po jednej klasie.
Speedy
Cytat
Różnica pomiędzy interfejsem a klasą abstrakcyjną to np taka, że możesz implementować wiele interfejsów a dziedziczyć po jednej klasie.


Nie tylko. W klasie abstrakcyjnej można zdefiniować całe ciało metody, a w interfejsie tylko jej prototyp (dostępność, nazwa, parametry).
sztosz
Ale to w takim razie sprowadza się do tego że do interfejs nie daje mi żadnej nowej funkcjonalności jaką dawał by mi klasa abstrakcyjna (inna sprawa że klasa abstrakcyjna nie daje mi niczego czego nie dawała by mi "zwykła" klasa). Bo co za różnica czy dziedziczę po x klas i implentuję y interfejsów, skoro mogę dziedziczyć po x+y klas. A tym samym nadal nie widzę realnej potrzeby zastosowania interfejsów.


Oczywiście nie chcę zaczynać dyskusji o wyższości kodowania z użyciem interfejsów nad kodowaniem bez ich użycia, bo już za duży ze mnie chłopczyk na bójki o niczym z piaskownicy.
Oraz oczywiście rozumiem że interfejsy, czy klasy abstrakcyjne pozwalają na uniknięcie pewnych błędów, ale z drugiej strony dobrze opisany kod + testy jednostkowe też na to pozwalają, i też o tym nie chcę rozmawiać.

Chce wiedzieć czy interfejs ma w jakieś unikalne zastosowanie którego gdzie nie da się go zastąpić niczym innym. Bo jak na razie dla mnie interfejs to taka klasa abstrakcyjna bez definicji metod, a klasa abstrakcyjna to taka klasa której obiektu nie można utworzyć i... tyle.
dr_bonzo
Cytat
Bo co za różnica czy dziedziczę po x klas i implentuję y interfejsów, skoro mogę dziedziczyć po x+y klas. A tym samym nadal nie widzę realnej potrzeby zastosowania interfejsów.

Cytat
Chce wiedzieć czy interfejs ma w jakieś unikalne zastosowanie którego gdzie nie da się go zastąpić niczym innym. Bo jak na razie dla mnie interfejs to taka klasa abstrakcyjna bez definicji metod, a klasa abstrakcyjna to taka klasa której obiektu nie można utworzyć i... tyle.


1. Chodzi o to ze mozesz dziedziczyc TYLKO po 1 klasie (pomijam np C++ gdzie mozesz po wielu, ale w nim nie masz interfejsow, ktore niejako zastepuja wielokrotne dziedziczenie).

2. Uzywasz edytora z podpowiadaniem skladni? Albo pokoduj troche w Javie/C# - wymuszanie okreslania typow podpowi ci po co sa interfejsy.

3.
Cytat
Oraz oczywiście rozumiem że interfejsy, czy klasy abstrakcyjne pozwalają na uniknięcie pewnych błędów, ale z drugiej strony dobrze opisany kod + testy jednostkowe też na to pozwalają, i też o tym nie chcę rozmawiać.

Tak, bez interfejsow wszytko nadal bedzie dzialac. Interfejsy w php sa wskazowka projektowa: czemu te 5 klas ma takie same metody? co musze zrobic zeby napisac 6ta klase, bo chce zmienic implementacje np. session handlera. W innych jezykach sa tez obejsciem okreslania typow dla zmiennych - bo masz N klas niepolaczonych ze soba, zadna nie dziedziczy po drugiej, ale maja wspolma metode, np validate(), no i jakos musisz okreslic typ przy definiowaniu zmiennej.
Uzywasz: Validator validator; gdzie Validator to nazwa interfejsu z metoda validate(). Dzieki temu kompilator wie jakich metod mozesz uzywac na obiektach przypisanych do tej zmiennej.
sztosz
Cytat(dr_bonzo @ 2.09.2009, 00:42:55 ) *
1. Chodzi o to ze mozesz dziedziczyc TYLKO po 1 klasie (pomijam np C++ gdzie mozesz po wielu, ale w nim nie masz interfejsow, ktore niejako zastepuja wielokrotne dziedziczenie).

2. Uzywasz edytora z podpowiadaniem skladni? Albo pokoduj troche w Javie/C# - wymuszanie okreslania typow podpowi ci po co sa interfejsy.

3.
Tak, bez interfejsow wszytko nadal bedzie dzialac. Interfejsy w php sa wskazowka projektowa: czemu te 5 klas ma takie same metody? co musze zrobic zeby napisac 6ta klase, bo chce zmienic implementacje np. session handlera. W innych jezykach sa tez obejsciem okreslania typow dla zmiennych - bo masz N klas niepolaczonych ze soba, zadna nie dziedziczy po drugiej, ale maja wspolma metode, np validate(), no i jakos musisz okreslic typ przy definiowaniu zmiennej.
Uzywasz: Validator validator; gdzie Validator to nazwa interfejsu z metoda validate(). Dzieki temu kompilator wie jakich metod mozesz uzywac na obiektach przypisanych do tej zmiennej.


Ad 1: OD KIEDY mogę dziedziczyć TYLKO po 1 klasie?
Ad 3: Ja nie pytam o konkretny język programowania tylko o teorię, po to temat w Hydeparku. Ale to jest właśnie przykład o który mi chodziło, dziękuję.
dr_bonzo
1. A od kiedy mozesz? biggrin.gif Na pewno *mozesz* w C++, w Javie nie mozesz, w PHP nie mozesz (http://www.php.net/manual/en/keyword.extends.php ) , w Rubym nie mozesz (tam masz moduly, cos blizej wielodziedziczeniu niz interfejsom), C# - tez nie (googlilem przed chwila)

3.
Cytat
Ad 3: Ja nie pytam o konkretny język programowania tylko o teorię,

Ciezko mowic o teorii kiedy kazdy jezyk jest inny. Mozemy mowic o interfejsach, kiedy za czasow powstawania C++ ich nie bylo.
Java powstala jako "better c++", pozbyli sie wielodziedziczenia, wprowadzili interfejsy itp.
LBO
Przede wszystkim interfejsy umożliwiają poprawną modularyzację aplikacji i ułatwiają modelowanie zależności pomiędzy obiektami. Są bardzo przydane przy projektowaniu modeli (domain models).

Pokaże to na prostym przykładzie wyszukiwarki produktów.
  1. interface IProductSearch
  2. {
  3. /**
  4.   * Add product to search engine index
  5.   *
  6.   * @throws Exception
  7.   */
  8. public index(IProduct $product);
  9.  
  10. /*
  11.   * @return IProduct[] array of products hit by search
  12.   * @throws Exception
  13.   */
  14. public search(array $product);
  15. }

Wszyscy wiemy jak wiele jest rozwiązań dotyczących wyszukiwarek, zatem:
  1. class LuceneProductSearch implements IProductSearch
  2. {
  3. public index(IProduct $product)
  4. {
  5. // tutaj używamy np. Zend_Search_Lucene lub Java Bridge
  6. }
  7.  
  8. public search(array $product)
  9. {
  10. // tutaj używamy np. Zend_Search_Lucene lub Java Bridge
  11. }
  12. }
  13.  
  14. // lub
  15.  
  16. class SphinxProductSearch implements IProductSearch
  17. {
  18. protected $pdo;
  19.  
  20. public function __construct(PDO $pdo)
  21. {
  22. $this->pdo = $pdo;
  23. }
  24.  
  25. public index(IProduct $product)
  26. {
  27. // Prawdopodobnie nic nie robimy i korzystamy z automatycznego indeksowania Sphinxa
  28. }
  29.  
  30. public search(array $product)
  31. {
  32. // wysyłamy odpowiednie zapytanie SQL do bazy
  33. }
  34. }

Użycie mogłoby wyglądać w ten sposób:
  1. // Tworzymy i ustawiamy obiekt produktu jak i wyszukiwarki
  2. $product->save($product_search);
  3. // lub
  4. $product->update($product_search);

W powyższym przykładzie jak krowie na rowie wymodelowaliśmy zależność Produkt-Wyszukiwarka przy zapisywaniu i aktualizacji obiektu. Ta zależność może wyglądać tak:
  1. // Teoretycznie można użyć ORM obiektu pod
  2. // warunkiem, że implementuje interfejs IProduct
  3. class DatabaseProduct implemnts IProduct
  4. {
  5. // etc
  6.  
  7. /**
  8.   * @return boolean true on success, false otherwise
  9.   */
  10. public function save(IProductSearch $product_search)
  11. {
  12. $this->pdo->beginTransaction();
  13. try {
  14.  
  15. // W tym miejscu zapisujemy produkt do bazy danych
  16.  
  17. $this->setId($this->pdo->lastInsertId());
  18. $product_search->index($this); // obiekt zapisany, teraz go zaindeksujmy w wyszukiwarce
  19. $this->pdo->commit();
  20. } catch(Exception $e) {
  21. $this->pdo->rollBack();
  22. return false;
  23. }
  24. return true;
  25. }
  26. }


Jeżeli do projektu jeszcze nie wybrano mechanizmu wyszukiwania, można by również zaimplementować pusty obiekt NoneProductSearch, który by zwyczajnie nic nie indeksował i wracał pustą tablicę przy wyszukiwaniu.

------------------------------------------------
Cały kłopot z programowaniem na interfejsach - jak również niechęć programistów - tkwi w instancjonowaniu obiektów. Trzeba się trochę nagimnastykować.

1. Operator new

  1. $product_search = new LuceneProductSearch;
  2. //lub
  3. $product_search = new SphinxProductSearch($this->getContext()->getDatabase()->getDatabaseConnection());


Zwyczajne tworzenie obiektów już nie wystarcza. Potrzeba czegoś bardziej elastycznego, bo można skończyć (np. przy przesiadce z Lucene na Sphinxa) zmieniając nazwę obiektu wyszukiwarki w każdym miejscu, gdzie się jej używa

2. Wzorce kreacyjne i pochodne
  1. $product_search = $this->getContext()->getProductSearch();


Już lepiej. Konfiguracja obiektu odbywa się w jednym miejscu, ale od ilości pisaniny (np. fabryka do każdego obiektu blink.gif ) można szlak trafić.

  1. // Dla uproszczenia załóżmy, że ta konkretna implementacja kontekstu jest
  2. // również fabryką modeli ProductSearch
  3. class myContext extends sfContext
  4. {
  5. // etc
  6.  
  7. /**
  8.   * @return IProductSearch
  9.   */
  10. public function getProductSearch()
  11. {
  12. return new SphinxProductSearch($this->getDatabaseConnection());
  13. }
  14. }


3. Bardziej kompleksowe rozwiązania.

Przykładem takowych są kontenery IoC. Całość tworzenia obiektów jest przekazywana do nich w formie np. wygodnej xmlowej konfiguracji.
  1. $product_repository = $container>get('ProductRepository');
  2. /* @var $product_repository IProductRepository */
  3.  
  4. $product = $productRepository->getProductById($request->getParameter('id'));
  5. /* @var $product IProduct */
  6.  
  7. $product_search = $container>get('ProductSearch'); // Zwraca odpowiednio skonfigurowane LuceneProductSearch lub SphinxProductSearch
  8. /* @var $product_search IProductSearch */
  9.  
  10. // W tym miejscu ustawiamy nowe wartości obiektu $product
  11.  
  12. $product->update($product_search); // et voila
  13. // lub w zalezności, kto jak podchodzi do repozytoriów
  14. $product_repository->save($product);

Cysiaczek
@sztosz - w OOP jest kilka tematów na temat interfejsów, niemal każdy traktuje o sensie ich istnienia. Są tam wymienione wszystkie zalety i wady stosowania interfejsów, wiec aprawde nie rozumiem, po co kolejny temat o tym samym :/
Przenoszę
sztosz
Cytat(dr_bonzo @ 2.09.2009, 01:25:23 ) *
1. A od kiedy mozesz? biggrin.gif Na pewno *mozesz* w C++, w Javie nie mozesz, w PHP nie mozesz, w Rubym nie mozesz,C# - tez nie


Ale w Pythonie mogę, w Perlu mogę.

Dziękuję wszystkim za komentarze.

Cysiaczek ma rację, niepotrzebnie zakładałem taki temat. Czytam i czytam i wychodzi na to że Interfejsy nie dają żadnej niezastępowalnej funkcjonalności.
Crozin
Cytat
Czytam i czytam i wychodzi na to że Interfejsy nie dają żadnej niezastępowalnej funkcjonalności.
W PHP podstawowym celem interface-ów jest zagwarantowanie, że to z czym masz do czynienia jest tym czego oczekujesz - przynajmniej w części z której będziesz korzystać.
I takiej funkcjonalności nie dają Ci żadne inne elementy języka.

btw: z wielokrotnym dziedziczeniem nie chodziło o coś takiego:
  1. class A{}
  2. class B extends A{}
  3. class C extends B{}
Tylko o
  1. class A{}
  2. class B{}
  3. class C extends A, B{}
Taka konstrukcja w PHP jest niemożliwa.
sztosz
Mnie fascynuje Python, PHP denerwuje. Do zagwarantowania że to z czym mam do czynienia jest tym czego oczekuję nie są mi potrzebne interfejsy a specyfikacja, dokumentacja raczej. Poza tym mogę zawsze dziedziczyć po klasie abstrakcyjnej, inna sprawa że nie ma klas abstrakcyjnych w Pythonie, ale to też jest banalnie poste do zaimplentowania. Teraz nasuwa się pytanie: Kiedy wyrzucany jest błąd związany z tym że klasa nie poprawnie implementuje jakiś interfejs, w momencie tworzenia obiektu klasy, czy.... no kiedy?

Cytat(Crozin @ 2.09.2009, 14:54:34 ) *
btw: z wielokrotnym dziedziczeniem nie chodziło o coś takiego:
  1. class A{}
  2. class B extends A{}
  3. class C extends B{}
Tylko o
  1. class A{}
  2. class B{}
  3. class C extends A, B{}
Taka konstrukcja w PHP jest niemożliwa.


Dla mnie wielokrotne dziedziczenie to coś takiego:
[PYTHON] pobierz, plaintext
  1. class A(object):
  2. def a(self):
  3. print 'a'
  4.  
  5. class B(object):
  6. def b(self):
  7. print 'b'
  8.  
  9. class C(A, B):
  10. pass
  11.  
  12. obiekt = C()
  13. obiekt.a()
  14. obiekt.b()
[PYTHON] pobierz, plaintext

rezultat:
Kod
a
b


I cały czas o tym mówię.
Crozin
Cytat
Mnie fascynuje Python, PHP denerwuje.
Mnie też PHP wieloma elementami denerwuje i staram się od niego odejść na rzecz m.in. Javy - ale jest to nadal potężne narzędzie, które oferuje na prawdę wiele.

Cytat
Do zagwarantowania że to z czym mam do czynienia jest tym czego oczekuję nie są mi potrzebne interfejsy a specyfikacja, dokumentacja raczej.
Dokumentacja dokumentacją, kod kodem.
  1. <?
  2. class A{
  3. public function abc(MyInterface $obj){
  4.  
  5. }
  6. }
Takie wymuszenie jest dużo wygodniejsze niż patrzenie po dokumentacji. Poza tym ewentualny błąd:
Cytat
Metoda A:abc() wymaga by pierwszy argument był MyInterface-m
Jest dużo wygodniejszy niż
Cytat
Nie można wykonać metody def() na obiekcie XXX - linia 123, plik abc
Cytat
Poza tym mogę zawsze dziedziczyć po klasie abstrakcyjnej
No właśnie klasy abstrakcyjnej nie możesz użyć zawsze... ba! Użycie jej jest możliwe jedynie w podstawowych przypadkach typu:
  1. <?
  2.  
  3. abstract class Shape{
  4. abstract public function getSomething();
  5. }
  6.  
  7. class Square extends Shape{
  8.  
  9. }
  10.  
  11. //...
  12. public function doSth(Shape $shape)
Co w przypadku gdy Square dziedziczy już po czymś? Nie możesz w nieskończoność dziedziczyć, bo w końcu byś sens tego stracił: Zwierzę -> Ssak -> Człowiek - to jak najbardziej ok. Zwierzę -> Porusza się -> Oddycha -> Mówi -> Ssak -> Człowiek? Ssaki są rozwinięciem "mowy"? Z interfaceami jest to logiczne: Zwierzę(porusza się, oddycha, mówi) -> Ssak -> Człowiek.
Cytat
inna sprawa że nie ma klas abstrakcyjnych w Pythonie, ale to też jest banalnie poste do zaimplentowania.
A w PHP nie ma klas wewnętrznych... ale co to ma do rzeczy?
Cytat
Kiedy wyrzucany jest błąd związany z tym że klasa nie poprawnie implementuje jakiś interfejs, w momencie tworzenia obiektu klasy, czy.... no kiedy?
W przypadku PHP - sprawdzanie czy dana klasa implementuje wszystkie interface-y wykonywane jest w momencie tworzenia obiektu - ale to w manualu można wyczytać.

Cytat
Dla mnie wielokrotne dziedziczenie to coś takiego:
W PHP coś takiego jest niemożliwe.
sztosz
No i to jest właśnie ten problem którego chciałem uniknąć. Założyłem temat na Hydeparku, a nie w PHP żeby mi nie tłumaczono co można w PHP a czego nie, bo to mnie zupełnie nie interesuje. Chciałem dowiedzieć się czegoś o interfejsów od bardziej doświadczonych forumowiczów, a i tak dowiaduję się "Co mi da używanie interfejsów w PHP". Jeśli zaraz nie padnie stwierdzenie w stylu "A czegoś się spodziewał, to przecież forum PHP!'' to będę zaskoczony.

Z tego tematu wynika że w PHP interfejs jest tylko pomocnikiem by programista widział co musi zaimplentować, natomiast i tak musi dla każdej klasy implementującej dany interfejs napisać ciało metody. Dziedzicząc zaś po kilku klasach (co w PHP jest nie możliwe) nie musimy tego robić, ale nic nie stoi na przeszkodzie przesłaniania metod klas po których dziedziczymy (owszem tu są tez niedogodności, ale tylko jeśli nieuważnie kodujemy).

[PYTHON] pobierz, plaintext
  1. class Czlowiek(PoruszaSie, Oddycha, Mowi, Ssak, Zwierze):
  2. pass
[PYTHON] pobierz, plaintext

I nie potrzeba mi interfejsów.
Crozin
Ahh... zapomniałem, że temat miał być ogólny - mój błąd. Przeglądałem przez "Pokaż najnowsze" i nie zwróciłem nawet uwagi na dział.

Wracając do przykładu z królestwem zwierząt. Co wg Ciebie miała by robić (abstrakcyjna) klasa "porusza się"? Przecież człowiek porusza się w zupełnie inny sposób niż bardzo podobna do niego małpa nie wspominając np. o psie czy delfinie (a wszystkie te zwierzęta dziedziczą po: Zwierzę -> Ssak). Tak więc nie ma tutaj po prostu czego dziedziczyć bo każdy z obiektów wykonuje to zupełnie inaczej. Ale z zewnątrz mamy tak jakby ten sam interface, bo każdy z tych wymienionych obiektów potrafi poruszyć się w prawo, w lewo, do przodu oraz do tyłu.
Jeżeli bardzo chciałbyś użyć tutaj dziedziczenia to musiałaby to być abstrakcyjna klasa definiująca jedynie abstrakcyjne metody, a język prog. musiałby umożliwić wielokrotne dziedziczenie. interface natomiast przynosi nam bardzo wygodny mechanizm identyfikacji "możliwości" obiektu:
  1. //powiedzmy, że jesteśmy wew. jakieś metody "obrony przed zagrożeniem"
  2. if($obj->poziomZycia < 30 && $obj instanceof MozeSiePoruszac){
  3. $obj->uciekajByleGdzie();
  4. }else{
  5. $obj->pozegnajSieZZyciem(); //niestety nasz obiekt jest grzybkiem, który nie może uciec
  6. }
Tak więc nawet w PHP (chociaż ten przykład jest od tego języka dosyć mocno oderwany) interface-y pełnią dużo większą rolę niż tylko "pomocnicza".
sztosz
O widzisz, i zaczynam poniekąd znajdować zastosowanie dla interfejsu.
dr_bonzo
Cytat
Z tego tematu wynika że w PHP interfejs jest tylko pomocnikiem by programista widział co musi zaimplentować, natomiast i tak musi dla każdej klasy implementującej dany interfejs napisać ciało metody. Dziedzicząc zaś po kilku klasach (co w PHP jest nie możliwe) nie musimy tego robić, ale nic nie stoi na przeszkodzie przesłaniania metod klas po których dziedziczymy (owszem tu są tez niedogodności, ale tylko jeśli nieuważnie kodujemy).


Tak smile.gif To prawda.
Interfejsy sa obejsciem wielokrotnego dziedziczenia (rozwiazaniem zaproponowanym zamiast W. D.), i ich wada jest to ze nie moga zawierac cial metod - bo tak ich tworcy sobie zalozyli smile.gif
Pewnie z perspektywy czasu nie jest to najlepsze rozwiazanie, i wielodziedziczenie sie przydaje (dla php, hmm, chyba mielem kilka chwil gdzie bym z niego skorzystal, ale nie jestem w stanie wymienic - o musialem zduplikowac kilka metod w dwoch roznych hierarchiach klas, a takto podziedziczylbym z 3ciej klasy i po problemie).

PHP poniekad wzorowalo sie na javie w OOP. Jednak w Javie oprocz interfejsow masz tez klasy wewnetrzne, ktore to pozwalaja w pewien sposob wykonac wielodziedziczenie, konkretniej - dodac do klasy zbioru metod z cialami, jednoczesnie kozystajac z pojedynczego dziedziczenia - http://www.javaworld.com/javaworld/jw-10-2...4-multiple.html .
vokiel
Osobiście dla mnie interfejsy są zasadne w kilku przypadkach:
Pluginy - gdy plugin musi mieć określone metody (instalacja, dodanie do menu w panelu, sprawdzenie wersji, wyswietlenie na stronie, etc). Interfejs wymusza zastosowanie tych metod, dzięki czemu wiem, że nie dołączę pluginu bez instalatora.

Drivery - ostatnio pisałem loggera. Przedstawię na przykładzie:
  1. <?php
  2. interface logInterface {
  3. public static function error($err);
  4. public static function warn($err);
  5. public static function info($err);
  6. }
  7.  
  8. class logFile implements logInterface {
  9. public static function error($err){
  10. // zapis błędu do pliku
  11. }
  12. public static function warn($err){
  13. // zapis ostrzeżenia do pliku
  14. }
  15. public static function info($err){
  16. // zapis informacji do pliku
  17. }
  18. }
  19. ?>

Każda następna klasa implementująca logInterface musi posiadać te 3 metody. Dzięki temu mogę utworzyć klasę log która będzie przyjmowała obiekty klas implementujących logInterface.
W przypadku zmian, dodaniu innego sterownika logów, użycie będzie zawsze takie samo:
  1. log::info($err);
Wystarczy na początku skryptu zmienić sterownik:
  1. log::addDriver(new logDb());
  2. log::useDriver('logDb');

I już mogę używać logowania do bazy zamiast do pliku, bez zmian w całym kodzie.

Można to osiągnąć przez użycie klasy abstrakcyjnej. W tym przypadku logowanie do pliku odbywa się zupełnie inaczej niż logowanie do bazy. Zatem klasa abstrakcyjna musiałby mieć tylko zarysy metod, bez ciała - co w rezultacie sprowadza się do interfejsu.

Interfejs dodaje warstwę abstrakcji, którą w przypadku zmian łatwo wymienić nie ingerując w cały kod wykorzystujący dane rozwiązanie.
sztosz
@vokiel: Ale do tego żeby to zrobić, nie potrzebujesz ani interfejsu, ani klasy abstrakcyjnej, wystarczy po prostu mieć dane metody w klasie sterownika loggera. Jak na razie wszyscy piszą o tym gdzie można użyć interfejsu, ale ja sam potrafię znaleźć miliony zastosowań dla interfejsu, lecz nie w tym rzecz. Jak na razie wiem, że w językach które nie mogą dziedziczyć po wielu klasach interfejs jest "kulawym"* zastępcą dziedziczenia po wielu klasach. Ale nie widzę nigdzie przykładu gdzie interfejs jest nie do zastąpienia w bardzo prosty sposób, a tego szukam.

Przykład ~Crozin'a z "object x is instance of Class Y" tam gdzie jest dziedziczenie po wielu klasach nie potrzebuje atrybutu, tam gdzie nie ma tego można i chyba nawet lepiej zastąpić to jakimś atrybutem klasy bazowej. Bo "możeChodzić" pasuje mi bardziej jako booleanowski atrybut a nie jakiaś klasa czy interfejs. W konstruktorze grzybka ustawiamy "możeChodzić = False" a mrówki "możeChodzić = True". Ale za daleko wybiegam chyba z myślami poza temat.


___________
*Kulawym ponieważ nie pozwala tworzyć w interfejsie ciała metody. Ale tak to zostało zaprojektowane i nie mnie oceniać czy to źle czy dobrze.
vokiel
Cytat
Ale nie widzę nigdzie przykładu gdzie interfejs jest nie do zastąpienia w bardzo prosty sposób, a tego szukam.
Raczej takiego przykładu nie znajdziesz, większość (jeśli nie wszystko) da się zrobić na wiele sposobów, czasem dookoła, ale jednak.

Ja osobiście, w przypadku, gdy nie zawrę metody w klasie implementującej po interfejsie wolę jak dostaję komunikat, który nawet pokaże jaki interfejs i jaka metoda została pominięta, niż gdy ma to się ujawnić w momencie próby użycia danej metody.
W przypadku aplikacji, która daje możliwość dodawania modułów. PHPdoc interfejsu jest dość wygodnym rozwiązaniem.

Tak sobie teraz myślę...
Interfejsy dają wielodziedziczenie, jednak tylko pozorne, bo ciała metod i tak trzeba pisać od nowa. Dopiero interfejsy + klasy abstrakcyjne dają możliwość dziedziczenia po wielu klasach z wykorzystaniem już napisanego kodu.
erix
Teraz moje trzy grosze. ;p

  1. $obj instanceof MozeSiePoruszac

Z praktycznego punktu widzenia, to chyba jedyny sensowny przykład użytkowania interfejsów w PHP właśnie z tego względu, że nie można definiować ciał metod.

Z tym MozeSiePoruszac, to może nieco abstrakcyjnie, ale można by było to zdefiniować mniej więcej tak: mamy jakąś bibliotekę, która jest zależna tylko od bazy; modele mogą operować na danych z DB albo i z innego źródła, więc modele można by implementować mniej więcej tak:

  1. class zwyczajnaModel implements dbModel {}
  2. class podwawelskaModel implements xmlModel {}


i np. w helperze sprawdzasz:

  1. $this->helperLoader('helperName', $modelObj);
  2. //... helper:
  3. function __construct($modelObj){
  4. if($modelObj instanceof xmlModel){
  5. ...
  6. }
  7. }


Jeśli chodzi o pozostałe przypadki, to w PHP interfejsy są raczej nieco ubogie w praktyczne zastosowanie i wymusza dodatkowe parsowanie kodu... Może się kiedyś doczekamy normalnych interfejsów. biggrin.gif
dr_bonzo
@sztosz

Cytat
Jak na razie wiem, że w językach które nie mogą dziedziczyć po wielu klasach interfejs jest "kulawym"* zastępcą dziedziczenia po wielu klasach. Ale nie widzę nigdzie przykładu gdzie interfejs jest nie do zastąpienia w bardzo prosty sposób, a tego szukam.



Obiektywnie interfejsy maja duzo mniejsza funkcjonalnosc niz klasy abstrakcyjne (nie maja cial metod).

W polaczeniu z konkretnym jezykiem programowania, interfejs i k.abstr. nabywaja nowych wlasciwosci.

Chcesz przyklad gdzie " interfejs jest nie do zastąpienia " ? - Uzyj, "wielodziedziczenia" w php, czy tez javie. Czemu interfejs jest tu lepszym rozwiazaniem? BO NIE MA INNEGO - w php nie masz wielodziedziczenia, rozszerzenia klasy juz uzyles, wiec musisz skorzystac z interfejsu.

Cytat
@vokiel: Ale do tego żeby to zrobić, nie potrzebujesz ani interfejsu, ani klasy abstrakcyjnej, wystarczy po prostu mieć dane metody w klasie sterownika loggera


No pewnie ze wystarcza metody, ale interfejs jest hmm, jak to nazwac, elementem projektowym, dokumentujacym ten zestaw metod jaki implementujesz.
Cysiaczek
Dziedziczenie to w większości przypadków zło wcielone. Tym bardziej dziedziczenie wielu z klas.
Nie na darmo jeden w twórców Javy (chyba nawet sam James Gosling) powiedział, że jeśli miałby coś zmienić w tym języku, to wywalił by dziedziczenie smile.gif
Interfejsy mają dać typ, metody, pomóc zorganizować projekt. Faktycznie przy codziennej klepaninie nie są przydatne, przynajmniej mi.

Pozdrawiam
sztosz
Cytat(Cysiaczek @ 4.09.2009, 10:22:14 ) *
Dziedziczenie to w większości przypadków zło wcielone. Tym bardziej dziedziczenie wielu z klas.
Nie na darmo jeden w twórców Javy (chyba nawet sam James Gosling) powiedział, że jeśli miałby coś zmienić w tym języku, to wywalił by dziedziczenie smile.gif


Ok, ale dlaczego dziedziczenie to zło?
Spawnm
Bo w większości przypadków jest zbędne , a początkujący OOPowcy wciskają je gdzie się da smile.gif
Oczywiście czasem jest przydatne ale czasem.
phpion
Pierwsze słyszę opinię, że dziedziczenie to zło. Moglibyście rozwinąć temat?
LBO
Cytat(phpion @ 4.09.2009, 12:05:17 ) *
Pierwsze słyszę opinię, że dziedziczenie to zło. Moglibyście rozwinąć temat?


  1. class Core
  2. {
  3. public function doSomething()
  4. {
  5. // etc
  6. }
  7. }


a dalej magia:

  1. class Router extends Core
  2. {
  3. // etc
  4. }
  5.  
  6. class Controller extends Core
  7. {
  8. // etc
  9. }


Bez żadnego pomyślunku.
nospor
Cytat
Bo w większości przypadków jest zbędne
latanie heliktoperem z domu do pracy też w większości przypadków jest zbędne. mam na podstawie tego rozumieć ze heliktoptery to zło wcielone?

bezsensowna argumentacja tongue.gif
phpion
Równie dobrze można powiedzieć, że klasy to aaevil.gif ponieważ można do nich wpakować czysto strukturalny kod.
sztosz
Używanie intefejsów jest zbędne, a prawie wszyscy PHPowcy wciskają je gdzie się da. tongue.gif

A co do samego dziedziczenia, to fajnie jest opisane po co to i gdzie się przydaje w Thinking in Java.
Spawnm
Czy napisałem że zło wcielone? Napisałem że przydatne czasem. Czyli latanie helikopterem aby ratować ludzi w górach a nie wycieczka po masło.
nospor
Cytat
Czy napisałem że zło wcielone?
No to spojrzmy:
padło pytanie: Ok, ale dlaczego dziedziczenie to zło
na ktore ty odpowiedziales: Bo w większości przypadków jest zbędne , a początkujący OOPowcy wciskają je gdzie się da
Oczywiście czasem jest przydatne ale czasem.

Czyli potwierdziles teze zapytania. a ze czasem przydatne...

Tak czy siak nadal gadasz/gadacie glupoty. Nie wyobrazam sobie OOP bez dziedziczenia. I nie, nie daje tego wszedzie gdzie sie da. Daje wszedzie tam gdzie potrzeba.
Do pracy tez nie latam heliktopterem, ale jakby trzeba było kogoś ratowac to i owszem. Wiec heliktopery sa potrzebne tongue.gif
phpion
Cytat(Spawnm @ 4.09.2009, 12:02:31 ) *
Oczywiście czasem jest przydatne ale czasem.

Stwierdzenie "czasem X ale czasem" jasno daje do zrozumienia, że zazwyczaj tak nie jest więc może nie wypieraj się teraz tego smile.gif Nie napisałeś, że to zło wcielone (napisał to ~Cysiaczek), ale podpisałeś się pod jego słowami.
Spawnm
OK. W takim razie rozwinę : dziedziczenie to zło w rękach początkującego OOPowca który lata helikopterem po statek aby dopłynąć do sklepu po masło tongue.gif
nospor
No to czego na tak ogolne pytanie odpowiadasz tylko o wybranym gronie uzytkownikow i nie zaznaczysz tego dokladnie.... winksmiley.jpg tylko flame sie tu rodzi niepotrzebnie winksmiley.jpg

Wracając do tematu:
Normalnie nie uzywam interfejsów. Uzywam ich jedynie w klasach/projektach gdy ktoś będzie mogł coś dopisywać swojego i podpinać pod moje. Interfejs zabezpiecza mnie przed tym, ze ktoś coś skopie w metodach i bedzie na mnie.
Przyklad:
napisałem klase cache. Do cache mozna podpiąc rozne sterowniki zapisu cache: pliki, baza danych, pamiec. Każdy może napisac wlasne sterowniki jakie mu przyjdą do głowy. Ja jedynie wymagam by implementowaly one moj interfejs - dzieki temu klasa moze bez problemu uzywac cudzysz sterowników - wiem, ze sie nie wywali na zlej nazwie metody czy na zlych argumentach.
plurr
Cytat(nospor @ 4.09.2009, 12:57:23 ) *
Wracając do tematu:
Uzywam ich jedynie w klasach/projektach gdy ktoś będzie mogł coś dopisywać swojego i podpinać pod moje. Interfejs zabezpiecza mnie przed tym, ze ktoś coś skopie w metodach i bedzie na mnie.


Podpisuję się pod tym, interfejsy to pewnego rodzaju drogowskazy, powodują że kod się sam dokumentuje. Chociaż czasem może być drażniące skakanie po plikach. Niektórzy zaczynają pisać systemy od zaplanowania fundamentów - interfejsów, a następnie biorą się za pisanie reszty, pozwala to nie odbiegać od ustalonej wcześniej ścieżki.

Mogę też podać pewien przykład implementacji interfejsów:
System oparty na pluginach, nie wiadomo kto będzie pisał nowe pluginy. Ale wiadomo że wstrzykując do klasy bazowej plugin wymagamy, aby posiadał pewne metody. Oczywiście można też użyć ReflectionApi, albo jedno i drugie.

Co do samego dziedziczenia, to wg mnie trzeba je ograniczać - zmiany w klasie po której dziedziczymy, mogą wymusić zmiany w klasach potomnych. Jeśli system posiada zbyt dużo powiązań między klasami to powstaje "obiektowe spagetti".
Crozin
Cytat
No i to jest właśnie ten problem którego chciałem uniknąć. Założyłem temat na Hydeparku, a nie w PHP żeby mi nie tłumaczono co można w PHP a czego nie, bo to mnie zupełnie nie interesuje
Tak więc nie traktuje tylko z perspektywy PHP, gdzie bez interface-ów dałoby się objeść.

W językach typu Java nie wyobrażam sobie pracy bez interface-ów - głównie dlatego, że same w sobie stanowią one osobny typ. Są one również (tu już można mówić o wszystkich językach obsługujących je) niezwykle przydane w przypadku, gdy programiści tworzą kod dla programistów (to co nospor opisał).

Co do "zła w postaci dziedziczenia". Również nie wyobrażam sobie pracy bez niego. A to, że ludzie potrafią źle używać jakiegoś narzędzia to nie argument za tym by uznać narzędzie samo w sobie złym...
Cysiaczek
Jeśli nieprecyzyjnie się wyraziłem, to sprostuję. Zgadzam się ze @Spawnmem. Chodzi o to, że większość zastosowanych w praktyce relacji dziedziczenia jest "implementacją złego projektu", a więc "złem wcielonym". Dlatego lepiej wszystkim mówić, że dziedziczenie to ZUO, bo może się 3 razy zastanowią nad relacjami w projekcie.
Nie używam dziedziczenia inaczej jak z klasy abstrakcyjnej. Jeśli już to robię ze zwykłej klasy, to znaczy, że pracuję z czyimś kodem, gdzie nie mam wyboru.
Kompozycja jest preferowaną metodą łączenia obiektów, ponieważ jest bardziej elastyczna. Dziedziczenie nie jest elastyczne - jest relacją bardzo sztywną. Prawdopodobnie z powodu nadużywania, Gosling (tak mi się zdaje, że On) powiedział co powiedział.

Pozdrawiam
batman
Interface jest idealnym przykładem różnicy między teorią, a praktyką.

W teorii interface stanowi punkty wejścia/wyjścia z "black box-a". Blavk box jest tutaj dowolnym tworem, w którym mogą zachodzić różne operacje, o których nie musimy nic wiedzieć. Nas interesuje jedynie w jaki sposób można wykonać operacje na tym pudełku oraz jak pobrać i dostarczyć do niego dane. I tutaj z pomocą przychodzi interface, który nas o tym informuje. W praktyce wszyscy wiemy jak to wygląda.

W teorii programowania obiektowego wszystko jest obiektem (czyli posiada jakiś typ). Bardzo często zdarza się, że dany element musi posiadać dwa lub więcej typów. Można to osiągnąć poprzez wielodziedziczenie (mądre głowy zarzuciły ten pomysł, czyli musiał się nie sprawdzić), stworzenie potężnego drzewa zależności (nie muszę chyba pisać jak głupi jest to pomysł) oraz dzięki interface-om.

Życiowym przykładem, w którym można wytłumaczyć sens istnienia interface-ów i ich zastosowanie jest operator sumowania. Weźmy taki przykład:
x + y
gdzie x = 1, a y = 2.
Wynikiem takiej operacji będzie 3. Ale skąd ma o tym wiedzieć kompilator/parser? Stąd, że do operatora przekazywane są dwie wartości o typach liczba. Jednak, skąd ten typ się wziął? Wziął się z interface-u, po którym dziedziczy(niejawnie) nasza zmienna. Poza typem liczba, nasza zmienna może dziedziczyć po interface np. sumowalna lub zmiennoprzecinkowa. Operator sprawdzając czy przekazana zmienna posiada jakiś typ, może wykonać stosowne operacje. Mechanizm ten można ostrożnie porównać do przeciążania metod.
sztosz
Chyba się mylisz ~batman. Parser/kompilator nic nie musi wiedzieć.
Kod
x = 1
y = 2
z = x + y

Jest tożsame z
Kod
x.=(1)
y.=(2)
z.=(x.+(y))


Przynajmniej w Pythonie i Rubym, wydaje mi się że w większość języków naprawdę obiektowych tak jest. Operatory to są de facto funkcje wbudowane w język. I nie ma żadnego chorego dziedziczenia po interfejsie zmiennoprzecinkowa ( O.o WTF?!?). Po co typ liczby zmiennoprzecinkowej ma dziedziczyć po jakimkolwiek interfejsie skoro taki interfejs, jak już do tego doszliśmy, jest tylko pomocą dla programisty a nie czymś tak ważnym dla samego języka jak np. zmienna, klasa, czy funkcja/metoda.
batman
~sztosz pytałeś czysto teoretycznie, abstrahując od konkretnej implementacji (przynajmniej tak to zrozumiałem). Jak to jest w konkretnym kompilatorze/parserze, trzeba już samemu pogrzebać.

A co w takiej sytuacji:
x = abc
y = 2
z = x + y

Co teraz ma zrobić kompilator/parser? Jeśli nie znałby typu (odziedziczonego po interface), wówczas niezłe rzeczy by się działy w pudełkach spod biurka winksmiley.jpg
sztosz
A czemu niby typ ma być dziedziczony po interfejsie?

Kompilator, parser sprawdza czy obiekt x posiada metodę =() i czy argumetnem tej metody może być string (w niektórych językach możliwe jest dodawanie stringów do intów i wtedy nie jawnie te inty są rzutowane na stringi, lub stringi na inty (jeśli te stringi to znaki z zakresu [0-9]), jeśli może to dodaje, jeśli nie to wywala wyjątek.

Pisałem że wiem jak to jest w Pythonie i Rubym, bo nie zagłębiałem się w implentacje tego w innych języków.

Ty gdzieś przeczytałeś o dziedziczeniu typów podstawowych po interfejsach czy sam to wymyśliłeś?
batman
Cytat
A czemu niby typ ma być dziedziczony po interfejsie?
A dlaczego nie? Interface po to między innymi został stworzony, by dany obiekt mógł mieć kilka typów.

Cytat
Kompilator, parser sprawdza czy obiekt x posiada metodę =() i czy argumetnem tej metody może być string (w niektórych językach możliwe jest dodawanie stringów do intów i wtedy nie jawnie te inty są rzutowane na stringi, lub stringi na inty (jeśli te stringi to znaki z zakresu [0-9]), jeśli może to dodaje, jeśli nie to wywala wyjątek.
Refaktorin mimo swoich niezaprzeczalnych zalet, ma jedną wadę - jest wolny. Znacznie szybciej sprawdzisz typ.

Cytat
Ty gdzieś przeczytałeś o dziedziczeniu typów podstawowych po interfejsach czy sam to wymyśliłeś?
Czytałem takie coś na blogu (źródła nie podam, nie zapisałem). Oczywiście rozważania autora były czysto teoretyczne - tak samo w moim przypadku. W końcu o to Ci chodziło?
LBO
batman, jak mniemam chodzi Tobie o to samo na czym opiera się składnia LINQ w .NET. Tak?

edit: przeformułowanie
Crozin
Cytat
skoro taki interfejs, jak już do tego doszliśmy, jest tylko pomocą dla programisty
Gdzie doszliśmy do takiego wniosku? Sam napisałem, że np. w Javie właściwie bez nich nie da się obejść - a jestem tego pewien, mimo iż nie mogę powiedzieć, że mam w tym języku jakieś doświadczenie.

batman
Cytat(LBO @ 5.09.2009, 18:55:59 ) *
batman, jak mniemam chodzi Tobie o to samo na czym opiera się składnia LINQ w .NET. Tak?

Nie. Chodzi mi tylko i wyłącznie o teorię. Bez żadnej implementacji.
LBO
Cytat(batman @ 5.09.2009, 18:59:34 ) *
Nie. Chodzi mi tylko i wyłącznie o teorię. Bez żadnej implementacji.


Z teorią to tu daleko nie zajdziesz.

Podstawowe LINQ opiera się całkowicie na interfejsach i tyle - każda implementacja działa zupełnie inaczej, więc nie ma co tworzyć klasy bazowej/abstrakcyjnej). +1 dla interfejsów.

Natomiast to o czym ty piszesz, czyli operatory w językach kompilowanych nie opierają się na interfejsach tylko na metodach, które można dopisać do własnych obiektów lub przeciążać.
batman
LBO
Cytat
Ja tylko niosę pomoc
winksmiley.jpg

Jeśli chciałbym pisać o .NET, to pisałbym w jego kontekście. Autor tematu prosił o wyjaśnienie po co są interface-y, a nie jak działają w konkretnym języku. Przykład z operatorem wydawał mi się najbliższy sercu programisty, więc go wykorzystałem. Nie chodziło mi o zanurzanie się w teorię przeciążania operatorów, LINQ, czy w konkretną implementację interface-ów. Moje rozważania są czysto teoretyczne i mają tyle wspólnego z rzeczywistością, co UML - z resztą napisałem to w moim pierwszym poście w tym temacie.
LBO
Cytat(batman @ 5.09.2009, 19:10:55 ) *
Przepraszam.


smile.gif

Jakby na to nie patrzeć czysto teoretycznie LINQ opiera się na wbudowanych w język operatorach i interfejsach z nich korzystających, o! Jest doskonałym przykładem na ich (interfejsów) poparcie ;P

Jedyny szkopuł w nieadekwatności do niedorobionej obiektówki PHP sad.gif

edit: doprecyzowanie
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.