Interfejsy pozwalają tworzyć kod definiujący metody klas, które go implementują. Nie można jednak dodać żadnego kodu do tych metod.
Klasy abstrakcyjne pozwalają robić to samo, dodając kod do metody.
Skoro możesz osiągnąć ten sam cel za pomocą klas abstrakcyjnych, dlaczego w ogóle potrzebujemy koncepcji interfejsów?
Powiedziano mi, że ma to związek z teorią OO od C ++ do Javy, na której oparte są rzeczy związane z OO PHP. Czy koncepcja jest przydatna w Javie, ale nie w PHP? Czy to tylko sposób na powstrzymanie się od wypełniania symboli zastępczych w klasie abstrakcyjnej? Czy coś brakuje?
Odpowiedzi:
Głównym celem interfejsów jest zapewnienie elastyczności zmuszania klasy do implementacji wielu interfejsów, ale nadal nie zezwalają na wielokrotne dziedziczenie. Problemy z dziedziczeniem z wielu klas są liczne i zróżnicowane, a wikipedia strona na ich temat całkiem dobrze je podsumowuje.
Interfejsy to kompromis. Większość problemów z wielokrotnym dziedziczeniem nie dotyczy abstrakcyjnych klas bazowych, więc większość współczesnych języków wyłącza wielokrotne dziedziczenie, ale wywołuje interfejsy abstrakcyjnych klas bazowych i pozwala klasie „zaimplementować” tyle z nich, ile chcą.
źródło
Ta koncepcja jest przydatna w programowaniu obiektowym. Dla mnie myślę o interfejsie jako umowie. Tak długo, jak moja klasa i twoja klasa zgadzają się na podpisanie umowy na tę metodę, możemy „interfejsu”. Jeśli chodzi o klasy abstrakcyjne, to te, które widzę jako bardziej podstawowe klasy, które usuwają niektóre metody i muszę uzupełnić szczegóły.
źródło
Dlaczego potrzebujesz interfejsu, jeśli istnieją już abstrakcyjne klasy? Aby zapobiec wielokrotnemu dziedziczeniu (może powodować wiele znanych problemów).
Jeden z takich problemów:
Źródło: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem
Dlaczego / Kiedy korzystać z interfejsu? Przykład ... Wszystkie samochody na świecie mają ten sam interfejs (metody) ...
AccelerationPedalIsOnTheRight()
,BrakePedalISOnTheLeft()
. Wyobraź sobie, że każda marka samochodów miałaby te „metody” inne niż inna marka. BMW miałoby Hamulce po prawej stronie, a Honda miałaby hamulce po lewej stronie koła. Ludzie będą musieli nauczyć się, jak działają te „metody” za każdym razem, gdy kupują inną markę samochodu. Dlatego dobrze jest mieć ten sam interfejs w wielu „miejscach”.Co robi dla ciebie interfejs (dlaczego ktoś miałby go używać)? Interfejs zapobiega popełnianiu „błędów” (zapewnia, że wszystkie klasy, które implementują określony interfejs, będą miały wszystkie metody dostępne w interfejsie).
W ten sposób
Create()
metoda będzie zawsze używana w ten sam sposób. Nie ma znaczenia, czy korzystamy zMySqlPerson
klasy, czy zMongoPerson
klasy. Sposób w jaki korzystamy z metody pozostaje taki sam (interfejs pozostaje taki sam).Na przykład będzie używany w ten sposób (wszędzie w naszym kodzie):
W ten sposób coś takiego nie może się wydarzyć:
O wiele łatwiej jest zapamiętać jeden interfejs i używać wszędzie tego samego interfejsu, niż wielu różnych.
W ten sposób wnętrze
Create()
metody może być różne dla różnych klas, bez wpływu na kod „zewnętrzny”, który wywołuje tę metodę. Cały zewnętrzny kod musi wiedzieć, że metodaCreate()
ma 1 parametr ($personObject
), ponieważ w ten sposób zewnętrzny kod użyje / wywoła metodę. Zewnętrzny kod nie obchodzi, co dzieje się w metodzie; musi tylko wiedzieć, jak go użyć / nazwać.Możesz to zrobić także bez interfejsu, ale jeśli używasz interfejsu, jest on „bezpieczniejszy” (ponieważ zapobiega popełnianiu błędów). Interfejs zapewnia, że metoda
Create()
będzie miała tę samą sygnaturę (te same typy i tę samą liczbę parametrów) we wszystkich klasach, które implementują interfejs. W ten sposób możesz być pewien, że DOWOLNA klasa, która implementujeIPersonService
interfejs, będzie miała metodęCreate()
(w tym przykładzie) i będzie potrzebowała tylko 1 parametru ($personObject
) do wywołania / użycia.Klasa implementująca interfejs musi implementować wszystkie metody, które interfejs wykonuje / posiada.
Mam nadzieję, że nie powtórzyłem się zbyt często.
źródło
Różnica między używaniem interfejsu a klasą abstrakcyjną ma dla mnie więcej wspólnego z organizacją kodu niż z wymuszaniem przez sam język. Używam ich często podczas przygotowywania kodu dla innych programistów do pracy, aby pozostawali w zamierzonych wzorcach projektowych. Interfejsy są rodzajem „projektu na podstawie umowy”, w którym kod zgadza się odpowiadać na określony zestaw wywołań API, które mogą pochodzić z kodu, do którego nie masz dostępu.
Podczas gdy dziedziczenie po klasie abstrakcyjnej jest relacją „jest”, nie zawsze jest to, czego chcesz, a implementacja interfejsu jest bardziej relacją „działa jak”. Różnica ta może być dość znacząca w niektórych kontekstach.
Załóżmy na przykład, że masz konto klasy abstrakcyjnej, z którego korzysta wiele innych klas (typy kont i tak dalej). Ma określony zestaw metod, które można zastosować tylko do tej grupy typów. Jednak niektóre z tych podklas kont implementują Versionable, Listable lub Editable, dzięki czemu można je wrzucić do kontrolerów, które oczekują używania tych interfejsów API. Kontroler nie dba o to, jaki to obiekt
Z drugiej strony, mogę również utworzyć obiekt, który nie rozciąga się na konto, powiedzmy klasę abstrakcyjną użytkownika, i nadal implementuję możliwość wyświetlania i edycji, ale nie wersji, co nie ma tutaj sensu.
W ten sposób mówię, że podklasa FooUser NIE jest kontem, ale NIE działa jak obiekt edytowalny. Podobnie BarAccount obejmuje konto, ale nie jest podklasą użytkownika, ale implementuje edytowalność, możliwość wyświetlania i również wersję.
Dodanie wszystkich tych interfejsów API dla edycji, listy i wersji do samych klas abstrakcyjnych byłoby nie tylko zaśmiecone i brzydkie, ale albo powieliłoby wspólne interfejsy konta i użytkownika, albo zmusiłoby mój obiekt użytkownika do implementacji wersji, prawdopodobnie tylko do rzucenia wyjątek.
źródło
Interfejsy są zasadniczo planem tego, co możesz stworzyć. Definiują metody, które musi mieć klasa , ale można tworzyć dodatkowe metody poza tymi ograniczeniami.
Nie jestem pewien, co masz na myśli mówiąc, że nie możesz dodać kodu do metod - bo możesz. Czy stosujesz interfejs do klasy abstrakcyjnej czy klasy, która ją rozszerza?
Metoda interfejsu zastosowana do klasy abstrakcyjnej będzie musiała zostać zaimplementowana w tej klasie abstrakcyjnej. Jednak zastosuj ten interfejs do klasy rozszerzającej, a metoda wymaga jedynie implementacji w klasie rozszerzającej. Mogę się tutaj mylić - nie używam interfejsów tak często, jak powinienem / powinienem.
Zawsze uważałem interfejsy za wzorzec dla zewnętrznych programistów lub dodatkowy zestaw reguł zapewniający poprawność.
źródło
Będziesz używać interfejsów w PHP:
$object instanceof MyInterface
Car
teraz obiekt castart()
,stop()
(EngineInterface) lubgoRight()
,goLeft()
(Interfejs sterowania )i inne rzeczy, o których nie mogę teraz myśleć
Numer 4 to prawdopodobnie najbardziej oczywisty przypadek użycia, którego nie można rozwiązać za pomocą klas abstrakcyjnych.
Z myślenia w Javie:
źródło
Interfejsy istnieją nie jako podstawa do rozszerzania klas, ale jako mapa wymaganych funkcji.
Oto przykład użycia interfejsu, w którym klasa abstrakcyjna nie pasuje:
Załóżmy, że mam aplikację kalendarza, która pozwala użytkownikom importować dane kalendarza ze źródeł zewnętrznych. Pisałbym klasy do obsługi importowania każdego typu źródła danych (ical, rss, atom, json) Każda z tych klas wdrożyłaby wspólny interfejs, który zapewniłby, że wszystkie mają wspólne metody publiczne, których potrzebuje moja aplikacja do uzyskania danych.
Następnie, gdy użytkownik doda nowy kanał, mogę zidentyfikować typ kanału i użyć klasy opracowanej dla tego typu, aby zaimportować dane. Każda klasa napisana w celu zaimportowania danych dla określonego kanału miałaby zupełnie inny kod, w przeciwnym razie może być bardzo niewiele podobieństw między klasami poza tym, że są one wymagane do wdrożenia interfejsu, który pozwala mojej aplikacji na ich wykorzystanie. Gdybym miał użyć klasy abstrakcyjnej, bardzo łatwo mogłem zignorować fakt, że nie przesłoniłem metody getEvents (), która następnie zepsułaby moją aplikację w tym przypadku, natomiast użycie interfejsu nie pozwoliłoby na uruchomienie mojej aplikacji, gdyby ŻADNA z metod zdefiniowane w interfejsie nie istnieją w klasie, która go zaimplementowała. Moja aplikacja nie musi dbać o to, jakiej klasy używa do pobierania danych z kanału,
Aby pójść o krok dalej, interfejs okazuje się niezwykle przydatny, gdy wracam do aplikacji kalendarza z zamiarem dodania innego rodzaju kanału. Korzystanie z interfejsu ImportableFeed oznacza, że mogę kontynuować dodawanie kolejnych klas, które importują różne typy plików danych, po prostu dodając nowe klasy, które implementują ten interfejs. To pozwala mi dodawać mnóstwo funkcji bez konieczności dodawania niepotrzebnie dużej ilości do mojej podstawowej aplikacji, ponieważ moja podstawowa aplikacja opiera się tylko na dostępnych publicznych metodach wymaganych przez interfejs, o ile moje nowe klasy importu kanałów implementują interfejs ImportableFeed, a następnie I wiem, że mogę po prostu go upuścić i kontynuować ruch.
To tylko bardzo prosty początek. Następnie mogę utworzyć inny interfejs, do którego wdrożenia będą wymagane wszystkie moje klasy kalendarza, który oferuje więcej funkcji specyficznych dla typu pliku danych, który obsługuje klasa. Innym dobrym przykładem może być metoda weryfikacji typu pliku danych itp.
To wykracza poza pytanie, ale ponieważ użyłem powyższego przykładu: interfejsy mają własny zestaw problemów, jeśli są używane w ten sposób. Uważam, że muszę upewnić się, że dane wyjściowe są zwracane z metod zaimplementowanych w celu dopasowania interfejsu. Aby to osiągnąć, używam środowiska IDE, które odczytuje bloki PHPDoc, i dodam typ zwracany jako podpowiedź do typu w bloku PHPDoc interfejsu, który następnie przetłumacz na konkretną klasę, która go implementuje. Moje klasy, które wykorzystują dane wyjściowe z klas implementujących ten interfejs, będą przynajmniej wiedziały, że oczekuje tablicy zwróconej w tym przykładzie:
Nie ma wiele miejsca na porównanie klas abstrakcyjnych i interfejsów. Interfejsy to po prostu mapy, które po zaimplementowaniu wymagają, aby klasa miała zestaw interfejsów publicznych.
źródło
Interfejsy służą nie tylko do upewnienia się, że programiści wdrażają określone metody. Chodzi o to, że ponieważ gwarantuje się, że klasy te posiadają pewne metody, możesz z nich korzystać, nawet jeśli nie znasz faktycznego typu klasy. Przykład:
W wielu przypadkach nie ma sensu przedstawianie klasy bazowej, abstrakcyjnej czy nie, ponieważ implementacje różnią się znacznie i nie współużytkują niczego oprócz kilku metod.
Języki o typie dynamicznym mają pojęcie „kaczego pisma”, w którym nie potrzebujesz interfejsów; możesz założyć, że obiekt ma wywoływaną metodę. To rozwiązuje problem w językach o typie statycznym, w których Twój obiekt ma jakąś metodę (w moim przykładzie read ()), ale nie implementuje interfejsu.
źródło
Moim zdaniem interfejsy powinny być preferowane zamiast niefunkcjonalnych klas abstrakcyjnych. Nie zdziwiłbym się, gdyby nastąpiłby tam hit wydajnościowy, ponieważ tworzy się tylko jeden obiekt zamiast analizować dwa i łączyć je (chociaż nie jestem pewien, nie znam wewnętrznego działania OOP PHP).
Prawdą jest, że interfejsy są mniej przydatne / znaczące niż w przypadku, powiedzmy, Java. Z drugiej strony PHP6 wprowadzi jeszcze więcej podpowiedzi typu, w tym podpowiedzi typu dla zwracanych wartości. To powinno zwiększyć wartość interfejsów PHP.
tl; dr: interfejsy definiuje listę metod, które należy stosować (think API), podczas gdy klasa abstrakcyjna zapewnia pewne podstawowe / wspólne funkcje, które podklasy dostosowują do konkretnych potrzeb.
źródło
W PHP możesz zastosować wiele interfejsów, oddzielając je przecinkami (myślę, że nie uważam tego za czyste rozwiązanie).
Jeśli chodzi o wiele klas abstrakcyjnych, możesz mieć wiele streszczeń rozciągających się nawzajem (znowu, nie jestem do końca tego pewien, ale myślę, że już gdzieś to widziałem). Jedyne, czego nie możesz przedłużyć, to klasa ostateczna.
źródło
Interfejsy nie zwiększą wydajności twojego kodu ani nic podobnego, ale mogą znacznie przyczynić się do jego utrzymania. Prawdą jest, że do ustanowienia interfejsu do kodu można użyć klasy abstrakcyjnej (lub nawet klasy nieabstrakcyjnej), ale odpowiednie interfejsy (zdefiniowane za pomocą słowa kluczowego i zawierające tylko sygnatury metod) są po prostu łatwiejsze do posortuj i przeczytaj.
Biorąc to pod uwagę, staram się używać dyskrecji przy podejmowaniu decyzji, czy użyć interfejsu nad klasą. Czasami chcę domyślnych implementacji metod lub zmiennych, które będą wspólne dla wszystkich podklas.
Oczywiście kwestia implementacji wielu interfejsów również jest rozsądna. Jeśli masz klasę, która implementuje wiele interfejsów, możesz użyć obiektu tej klasy jako różnych typów w tej samej aplikacji.
Fakt, że twoje pytanie dotyczy PHP, sprawia, że jest to trochę bardziej interesujące. Pisanie na interfejsach wciąż nie jest niezwykle potrzebne w PHP, gdzie można dosłownie dowolnie nakarmić dowolną metodę, niezależnie od jej typu. Możesz statycznie wpisać parametry metody, ale niektóre z nich są zepsute (ciąg, jak sądzę, powoduje pewne czkawki). Połącz to z faktem, że nie możesz wpisać większości innych referencji, a próba wymuszenia pisania statycznego w PHP ( w tym momencie ) nie ma dużej wartości . Z tego powodu wartość interfejsów w PHP , w tym momenciejest o wiele mniej niż w silniej pisanych językach. Mają zaletę czytelności, ale niewiele więcej. Wielokrotne wdrożenie nie jest nawet korzystne, ponieważ nadal musisz zadeklarować metody i nadać im treści wewnątrz implementatora.
źródło
Poniżej znajdują się punkty dotyczące interfejsu PHP
Przykładowy kod:
źródło
Widzieliśmy, że abstrakcyjne klasy i interfejsy są podobne, ponieważ zapewniają abstrakcyjne metody, które muszą być zaimplementowane w klasach potomnych. Jednak nadal mają następujące różnice:
Mam nadzieję, że pomoże to każdemu zrozumieć!
źródło
Interfejsy są jak twoje geny.
Zajęcia abstrakcyjne są jak twoi prawdziwi rodzice.
Ich cele są dziedziczne, ale w przypadku klas abstrakcyjnych a interfejsów dziedziczone są bardziej szczegółowe.
źródło
Nie znam innych języków, jaka jest tam koncepcja interfejsu. Ale w przypadku PHP postaram się jak najlepiej to wyjaśnić. Po prostu bądź cierpliwy i proszę o komentarz, jeśli to pomogło.
Zasada
A teraz weźmy przykład. Załóżmy, że mamy dwie zabawki: jedna to pies, a druga to kot.
Jak wiemy pies szczeka, a miauczenie kotów. Obaj mają tę samą metodę mówienia, ale mają inną funkcjonalność lub implementację. Załóżmy, że dajemy użytkownikowi pilot zdalnego sterowania z przyciskiem mówienia.
To dobry przypadek użycia interfejsu, a nie klasy abstrakcyjnej, ponieważ implementacje są różne. Czemu? Zapamiętaj
Jeśli potrzebujesz wesprzeć klasy potomne poprzez dodanie jakiejś nieabstrakcyjnej metody, powinieneś użyć klas abstrakcyjnych. W przeciwnym razie interfejsy byłyby twoim wyborem.
źródło