Jakie są różnice między klasami abstrakcyjnymi, interfejsami i kiedy z nich korzystać


15

Ostatnio zacząłem owijać głowę wokół OOP i teraz jestem do tego stopnia, że ​​im więcej czytam o różnicach między klasami abstrakcyjnymi i interfejsami, tym bardziej się mylę. Jak dotąd nie można utworzyć żadnej z nich. Interfejsy są mniej więcej strukturalnymi planami, które określają szkielet, a streszczenia są różne, ponieważ są w stanie częściowo zaimplementować kod.

Chciałbym dowiedzieć się o nich więcej w mojej konkretnej sytuacji. Oto link do mojego pierwszego pytania, jeśli chcesz trochę więcej podstawowych informacji: Jaki jest dobry model projektowania dla mojej nowej klasy?

Oto dwie klasy, które utworzyłem:

class Ad {
    $title;
    $description
    $price;

    function get_data($website){  }

    function validate_price(){  }
 }


class calendar_event {
    $title;
    $description

    $start_date;

    function get_data($website){ //guts }

    function validate_dates(){ //guts }
 }

Jak widać, klasy te są prawie identyczne. Nie pokazano tutaj, ale istnieją inne funkcje like get_zip(), save_to_database()które są wspólne dla moich klas. Dodałem również inne klasy Samochody i Zwierzęta, które mają wszystkie typowe metody i oczywiście właściwości właściwe dla tych klas (na przykład przebieg, waga).

Teraz naruszyłem zasadę DRY i zarządzam tym samym kodem w wielu plikach. Zamierzam mieć więcej klas, takich jak łodzie, konie itp.

Czy to tutaj użyłbym interfejsu lub klasy abstrakcyjnej? Z tego, co rozumiem na temat klas abstrakcyjnych, użyłbym superklasy jako szablonu ze wszystkimi wspólnymi elementami wbudowanymi w klasę abstrakcyjną, a następnie dodałbym tylko elementy szczególnie potrzebne w przyszłych klasach. Na przykład:

abstract class content {
    $title;
    $description


    function get_data($website){  }

    function common_function2() { }
    function common_function3() { }
 }


class calendar_event extends content {

    $start_date;

    function validate_dates(){  }
 }

Czy też użyłbym interfejsu, a ponieważ są one do siebie podobne, utwórz strukturę, z której każda z podklas jest zmuszona korzystać ze względu na integralność, i pozostaw to programistom końcowym, którzy opracują tę klasę, aby była odpowiedzialna za każdą z szczegóły nawet popularnych funkcji. sądzę, że niektóre „wspólne” funkcje mogą wymagać dostosowania w przyszłości dla potrzeb ich konkretnej klasy.

Mimo wszystko powyższego, jeśli uważasz, że nie rozumiem co i dlaczego abstrakcyjnych klas i interfejsów w ogóle, na wszelki sposób pozwól, aby ważna odpowiedź przestała myśleć w tym kierunku i zaproponowała właściwy sposób, aby iść naprzód!

Dzięki!


Istnieje wiele dobrych przykładów online. Myślę, że to jedna z nich. javapapers.com/core-java/abstract-and-interface-core-java-2/…
Siva

Odpowiedzi:


26

W kategoriach laika:

Interfejsy służą do relacji typu „można zrobić / można traktować jako” .

Klasy abstrakcyjne (jak również konkretne) są dla „to” rodzaj relacji.

Spójrz na te przykłady:

class Bird extends Animal implements Flight;
class Plane extends Vehicle implements Flight, AccountableAsset;
class Mosquito extends Animal implements Flight;
class Horse extends Animal;
class RaceHorse extends Horse implements AccountableAsset;
class Pegasus extends Horse implements Flight;

Bird, MosquitoI Horse Animals . Oni są spowinowaceni. Dziedziczą popularne metody od zwierząt eat(), metabolize() and reproduce(). Może zastępują te metody, dodając do nich trochę więcej, ale korzystają z domyślnego zachowania zaimplementowanego w AnimalmetabolizeGlucose().

Planenie jest powiązany z Bird, Mosquitolub Horse.

Flightjest implementowany przez niepowiązane, niepowiązane klasy, takie jak Birdi Plane.

AccountableAssetjest także implementowany przez niepowiązane, niepowiązane klasy, takie jak Planei RaceHorse.

Horse nie implementuje Flight.

Jak widać klasy (abstrakcyjne lub konkretne) pomagają budować hierarchie , pozwalając na dziedziczenie kodu z wyższych poziomów na niższe poziomy hierarchii. Teoretycznie im niżej jesteś w hierarchii, tym bardziej wyspecjalizowane jest twoje zachowanie, ale nie musisz się martwić o wiele rzeczy, które już są załatwione.

Z drugiej strony interfejsy nie tworzą żadnej hierarchii, ale mogą pomóc w homogenizacji określonych zachowań w różnych hierarchiach, dzięki czemu można je wyodrębnić z hierarchii w określonych kontekstach.

Na przykład możesz mieć sumę programową wartości grupy AccountableAssetsniezależnie od ich istnienia RaceHorseslub Planes.


Niesamowity. Interfejs zapewnia pewną sekcję funkcjonalności, która określa, can do/can be treated asgdzie klasy abstrakcyjne działają jako fundamenty!
Abhiroj Panwar

13

Możesz wydedukować odpowiedź logicznie, ponieważ zdajesz sobie sprawę z różnic między nimi.

Interfejsy definiują wspólną umowę. Na przykład interfejs o nazwie IAnimal, w którym wszystkie zwierzęta mają takie same funkcje, jak Eat (), Move (), Attack () itp. Podczas gdy wszystkie mają te same funkcje, wszystkie lub większość z nich ma inny sposób (implementację) osiągnięcia to.

Klasy abstrakcyjne definiują wspólne wdrożenie i opcjonalnie wspólne umowy. Na przykład prosty kalkulator może zostać uznany za klasę abstrakcyjną, która implementuje wszystkie podstawowe operatory logiczne i bitowe, a następnie zostaje rozszerzony o ScientificCalculator, GraphicalCalculator i tak dalej.

Jeśli masz wspólną implementację, to za wszelką cenę obuduj funkcjonalność w klasie abstrakcyjnej, z której chcesz rozszerzyć. Mam prawie 0 PHP doświadczenia, ale nie sądzę, że można tworzyć interfejsy z polami niestałymi. Jeśli pola są wspólne między klasami instancji, wówczas musisz użyć klasy Abstract, chyba że zdefiniujesz dostęp do nich za pomocą metod pobierających i ustawiających.

Ponadto wydaje się, że wyników w Google nie brakuje.


3

Krótko mówiąc. Klasy abstrakcyjne są bardzo podobne do interfejsów, ponieważ oba zapewniają szablon metod, które powinny znajdować się w klasie dziedziczącej, ale istnieją duże różnice: - Interfejsy definiują tylko nazwy / typy metod, które muszą istnieć w klasie dziedziczącej, podczas gdy abs- klasy mogą mieć pełny domyślny kod metody i tylko szczegóły mogą wymagać obejścia. - Interfejsy nie mogą mieć modyfikatorów dostępu. - Interfejsy nie mogą mieć pól. - Klasy nie mogą mieć wielokrotnego dziedziczenia klas, podczas gdy mogą dziedziczyć wiele interfejsów. - Ponadto klasy zapewniają strukturę hierarchiczną, dzięki czemu tylko klasy pochodzące z konkretnej klasy muszą przestrzegać wytycznych klasy abstrakcyjnej: obiekt-> konkretny obiekt-> bardzo konkretny obiekt. Z drugiej strony interfejsy mogą być dziedziczone przez każdego w dowolnym miejscu.

Moim zdaniem klasy abstrakcyjne są bardziej powszechne, ponieważ mogą od razu zapewniać domyślną implementację kodu, ale w projektach na dużą skalę, w których trzeba standaryzować pewne klasy, interfejsy mogą się przydać.

Mam nadzieję, że to pomaga, ale Leo ma wiele informacji na ten temat w Internecie


2
Classes cannot have multiple inheritance- Prawda dla języków takich jak Java i C #, nieprawda dla C ++.
Robert Harvey

3

Po pierwsze, powinieneś zrozumieć, że często zapewnisz zarówno interfejs, jak i klasę abstrakcyjną. Powodem tego i zasadniczą różnicą między nimi jest to, że pozwalają one na ponowne użycie innego kodu, a tym samym rozwiązanie różnych problemów.

Interfejsy pozwalają ponownie wykorzystywać kod klienta z różnymi implementacjami. Klient klasy get_data ($ website) nie dba o elementy $ title ani $ description. Po prostu chce poinstruować twoją treść, aby ładowała dane. Jeśli masz różne typy treści, niektóre z nich wymagają opisu $, a niektóre nie, możesz podać klasę ContentInterface, która określa tylko podpis klas podrzędnych. Teraz klient może mieć dowolną liczbę różnych treści, nie wiedząc dokładnie, jak działają. Liskov Zmiana Zasada jest dobrą rzeczą, aby przeczytać o zbadanie tej idei. Lubię też pisanie wuja Boba na ten temat. Interfejsy są bardzo ważne dla testów jednostkowych, a tworzenie interfejsów jest dobrym nawykiem do nauki.

Klasy abstrakcyjne pozwalają ponownie wykorzystywać wspólne szczegóły implementacji w zestawie klas, które mają wspólnego przodka. W swoim pytaniu wydaje się, że dobrze rozumiesz, dlaczego dziedziczysz implementację po klasie abstrakcyjnej. Wciąż niebezpieczne jest poleganie na wewnętrznych elementach klasy podstawowej - bardzo łatwo jest złamać enkapsulację i stworzyć dzieci, które zależą od konkretnych szczegółów implementacji klasy podstawowej. Metoda szablonowa zapewnia wspólny i zdrowe przykład jak używać klas bazowych bez naruszenia enkapsulacji.

Tak więc, jak mam nadzieję, że pokazałem, często udostępniasz interfejsy dla klientów z twojej hierarchii klas, abyś mógł bezpiecznie zmienić swoją implementację bez wpływu na kod klienta. Dzięki temu klient może pisać testy jednostkowe przy użyciu obiektów próbnych, które dziedziczą interfejs. Zapewnisz także klasy abstrakcyjne, które pozwolą na ponowne użycie wspólnej logiki lub wymuszą semantykę dla klas potomnych.


3

Różnica jest subtelna, ale wyraźna. Interfejs dotyczy zachowania polimorficznego. Klasa abstrakcyjna dotyczy ponownego użycia i zachowania polimorficznego.

Jeśli chcesz położyć nacisk na ponowne użycie i zachowanie polimorficzne, wybierz klasę abstrakcyjną. Na przykład pracownicy różnych typów mają różne przepisy, ale wszyscy otrzymują kilka wspólnych. Tak, klasa abstrakcyjna jest odpowiednia do jej reprezentowania bo podobieństw może być wyrażona w bazowym klasy abstrakcyjnej Employee, a różnica może być realizowane w klasach pochodnych, takich jak Managerlub Workeretc.

Jeśli chcesz położyć nacisk tylko na zachowanie polimorficzne, wybierz interfejs. Interfejs bardziej dotyczy umowy, tj. Obiektu lub hierarchii, który mówi, że jest zgodny z określonym zachowaniem. Na przykład wszyscy pracownicy mają rezerwę na urlop, ale różni pracownicy mają różne rodzaje rezerw. Tak więc każdy inny typ pracownika wymaga innego kalkulatora urlopu. Tutaj interfejs jest dobrym wyborem, ponieważ wszystkie typy pracowników mogą implementować LeaveCalculatorinterfejs z Calculate()zachowaniem w inny sposób.


-3
  1. Główna różnica polega na tym, że metody interfejsu Java są domyślnie abstrakcyjne i nie mogą mieć implementacji. Klasa abstrakcyjna Java może mieć metody instancji, które implementują domyślne zachowanie.
  2. Zmienne zadeklarowane w interfejsie Java są domyślnie ostateczne. Klasa abstrakcyjna może zawierać nie-końcowe zmienne.
  3. Członkowie interfejsu Java są domyślnie publiczni. Klasa abstrakcyjna Java może mieć zwykłe smaki członków klasy, takie jak prywatny, chroniony itp.
  4. Interfejs Java powinien zostać zaimplementowany przy użyciu słowa kluczowego „implements”; Klasa abstrakcyjna Java powinna zostać rozszerzona za pomocą słowa kluczowego „extends”.
  5. Interfejs może rozszerzyć inny interfejs Java, tylko klasa abstrakcyjna może rozszerzyć inną klasę Java i zaimplementować wiele interfejsów Java.
  6. Klasa Java może implementować wiele interfejsów, ale może rozszerzyć tylko jedną klasę abstrakcyjną.
  7. Interfejs jest absolutnie abstrakcyjny i nie można go tworzyć; Nie można również utworzyć instancji klasy abstrakcyjnej Java, ale można ją wywołać, jeśli istnieje main ().
  8. W porównaniu z klasami abstrakcyjnymi Java, interfejsy Java są wolne, ponieważ wymagają dodatkowej pośredniczości.
  9. Interfejs i klasa abstrakcyjna w Javie polega na tym, że nie można utworzyć metody nieabstrakcyjnej w interfejsie, każda metoda w interfejsie jest domyślnie abstrakcyjna, ale można utworzyć metodę nieabstrakcyjną w klasie abstrakcyjnej.
  10. Klasa abstrakcyjna vs. interfejs w Javie polega na tym, że interfejsy lepiej nadają się do deklaracji typu, a klasa abstrakcyjna bardziej nadaje się do ponownego wykorzystania kodu i perspektywy ewolucji.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.