Interfejs vs Klasa abstrakcyjna (ogólne OO)


1413

Niedawno miałem dwa wywiady telefoniczne, w których zapytano mnie o różnice między klasą interfejsu a klasą abstrakcyjną. Wyjaśniłem każdy aspekt, o którym mogłem pomyśleć, ale wygląda na to, że czekają, aż wymienię coś konkretnego i nie wiem, co to jest.

Z mojego doświadczenia wynika, że ​​następujące są prawdziwe. Jeśli brakuje jakiegoś ważnego punktu, proszę dać mi znać.

Berło:

Każda metoda zadeklarowana w interfejsie będzie musiała zostać zaimplementowana w podklasie. W interfejsie mogą istnieć tylko zdarzenia, delegaci, właściwości (C #) i metody. Klasa może implementować wiele interfejsów.

Klasa abstrakcyjna:

Podklasa musi implementować tylko metody abstrakcyjne. Klasa Abstract może mieć normalne metody z implementacjami. Klasa abstrakcyjna może również mieć zmienne klasowe obok Zdarzenia, Delegaci, Właściwości i Metody. Klasa może implementować tylko jedną klasę abstrakcyjną tylko z powodu nieistnienia Multi-dziedziczenia w C #.

  1. Po tym wszystkim ankieter zapytał: „Co by było, gdybyś miał klasę abstrakcyjną z tylko abstrakcyjnymi metodami? Czym różni się to od interfejsu?” Nie znałem odpowiedzi, ale myślę, że jest to dziedzictwo, jak wspomniano powyżej, prawda?

  2. Inny ankieter zapytał mnie, co jeśli miałbyś zmienną publiczną w interfejsie, czym by to się różniło niż w klasie abstrakcyjnej? Upierałem się, że nie możesz mieć publicznej zmiennej w interfejsie. Nie wiedziałam, co chciał usłyszeć, ale też nie był zadowolony.

Zobacz także :


412
Chociaż myślę, że ważne jest, aby znać różnicę między nimi, to nie jest dobre pytanie do wywiadu, imo. Chyba że zadaniem było pisanie książki na tematy związane z OO. Lepiej nie pracuj dla tych nietoperzy.
Alan

107
@Alan: Właściwie to podoba mi się to jako pytanie do wywiadu, ale nie chciałbym w ten sposób kogoś prześladować - prawdopodobnie napisałbym to bardziej w stylu „Gdzie wybrałbyś interfejs zamiast abstrakcyjnej klasy bazowej podczas definiowania hierarchii? ”lub coś podobnego.
Reed Copsey

11
Może szukali odpowiedzi bardziej skoncentrowanej na projektowaniu ... chociaż tak jak ty, potraktowałbym to jako pytanie techniczne.
CurtainDog,

16
Ładne różnice tabelaryczne tutaj: mindprod.com/jgloss/interfacevsabstract.html
Rajat_R

30
@Kave: I insisted you can't have a public variable inside an interface.Myślę, że interfejs może mieć zmienną publiczną. W rzeczywistości zmienne w interfejsie są automatycznie publiczne i ostateczne.
uczeń

Odpowiedzi:


746

Chociaż twoje pytanie wskazuje, że dotyczy „ogólnego OO”, wydaje się, że naprawdę koncentruje się na użyciu tych terminów w .NET.

W .NET (podobnie jak Java):

  • interfejsy nie mogą mieć stanu ani implementacji
  • klasa implementująca interfejs musi zapewniać implementację wszystkich metod tego interfejsu
  • klasy abstrakcyjne mogą zawierać stan (elementy danych) i / lub implementację (metody)
  • klasy abstrakcyjne mogą być dziedziczone bez implementacji metod abstrakcyjnych (chociaż taka klasa pochodna jest samą abstrakcją)
  • interfejsy mogą być wielokrotnie dziedziczone, klasy abstrakcyjne mogą nie (jest to prawdopodobnie kluczowy konkretny powód istnienia interfejsów oddzielnie od klas abtract - pozwalają na implementację wielokrotnego dziedziczenia, które usuwa wiele problemów ogólnego MI).

Jako ogólne warunki OO różnice niekoniecznie są dobrze zdefiniowane. Na przykład są programiści C ++, którzy mogą posiadać podobne sztywne definicje (interfejsy są ścisłym podzbiorem klas abstrakcyjnych, które nie mogą zawierać implementacji), podczas gdy niektórzy mogą powiedzieć, że klasa abstrakcyjna z niektórymi domyślnymi implementacjami jest nadal interfejsem lub że nie jest abstrakcyjna klasa nadal może definiować interfejs.

Rzeczywiście, istnieje idiom języka C ++ o nazwie Non-Virtual Interface (NVI), w którym metody publiczne są metodami innymi niż wirtualne, które „uderzają” w prywatne metody wirtualne:


7
Dziękuję Ci. Myślę, że skoro twoja odpowiedź wspomina o stanie + dobry przegląd całej reszty, zaznaczam twoją odpowiedź jako odpowiedź końcową. Masz rację, poprosiłem o ogólne OO, ponieważ mój pierwszy wywiad przeprowadził zapytanie o ogólne OO, ale ponieważ jestem facetem z C #, często o tym zapominam. ;-) Również dziękuję za wyjaśnienie C ++, ponieważ zawsze c ++ jest oszałamiający.
Houman

6
Myślę, że kluczowym punktem w wyjaśnieniu Michała jest to, że podczas implementacji interfejsu MUSISZ zaimplementować wszystkie elementy interfejsu, ale podczas dziedziczenia po klasie abstrakcyjnej NIE jest WYMAGANE przez klasę potomną do implementacji członków jej rodziców
Guillermo Gomez

82
+1: Chciałbym się założyć, że małpy prowadzące wywiad nawet nie zdają sobie sprawy, że inne języki wdrażają OO inaczej.
Wyścigi lekkości na orbicie

2
@JL Nie wiem, gdzie leży problem. Wygląda na to, że pomyliłeś metodę abstrakcyjną z klasą abstrakcyjną. Metody abstrakcyjne nie mają implementacji. Jednak wewnątrz abstrakcyjnej klasy , niektóre metody mogą być abstrakcyjne (czyli bez realizacji), podczas gdy inni mogą rzeczywiście mieć wdrożenie.
xji

19
Zauważ, że w Javie 8 możesz teraz mieć domyślne metody i metody statyczne w interfejsach, co oznacza, że ​​interfejsy Java mogą mieć implementację. Odnośnik tutaj . Oczywiście odnosiłeś się głównie do .NET, więc jest to tylko obserwacja odnosząca się do Javy.
davtom

866

Co powiesz na analogię: kiedy byłem w lotnictwie, poszedłem na szkolenie pilotów i zostałem pilotem USAF (US Air Force). W tym momencie nie miałem kwalifikacji do latania i musiałem uczestniczyć w szkoleniu na typ samolotu. Kiedy się zakwalifikowałem, byłem pilotem (klasa abstrakcyjna) i pilotem C-141 (klasa konkretna). Podczas jednego z moich zadań przydzielono mi dodatkowy obowiązek: funkcjonariusz ds. Bezpieczeństwa. Teraz nadal byłem pilotem i pilotem C-141, ale również pełniłem obowiązki funkcjonariusza ds. Bezpieczeństwa (wdrożyłem, że tak powiem, ISafetyOfficer). Pilot nie musiał być oficerem bezpieczeństwa, inne osoby również mogły to zrobić.

Wszyscy piloci USAF muszą przestrzegać określonych przepisów dla całego lotnictwa, a wszyscy piloci C-141 (lub F-16 lub T-38) są pilotami USAF. Każdy może być oficerem bezpieczeństwa. Podsumowując:

  • Pilot: klasa abstrakcyjna
  • C-141 Pilot: klasa betonu
  • ISafety Officer: interfejs

dodano uwagę: miała to być analogia do wyjaśnienia pojęcia, a nie zalecenie dotyczące kodowania. Zobacz różne komentarze poniżej, dyskusja jest interesująca.


87
Naprawdę podoba mi się ta analogia, wykorzystuje prosty przykład, aby wyjaśnić nieco złożony temat
Kevin Bowersox

13
To najlepszy sposób na zrozumienie złożonej terminologii OO. Krótko mówiąc, cała teoria jest warta tylko wtedy, gdy można z niej praktycznie skorzystać. @ Może powtórka jest naprawdę łatwa do uchwycenia, a następnie kilka punktów kuli (głównie przenikliwy umysł zamiast wchłonięcia!)
porównaniu do

54
Nadal jestem trochę zdezorientowany. Powiedzmy, że masz teraz kwalifikacje F-16 i T-38, więc klasa Jaynie może dziedziczyć po wielu klasach (pilot C-141, pilot F-16 i pilot T-38), czy to znaczy, czyje klasy powinny zostać interfejsami? Dzięki
Alex Okrushko

37
Wielu ludzi słusznie dało +1 komentarzowi Alexa, ponieważ pokazuje on pewną słabość w tym przykładzie. Po pierwsze, powiedziałbym, że Jay byłby instancją C-141Pilot, a nie własną klasą. Dodatkowo, ponieważ w USAF 99% wszystkich pilotów jest zakwalifikowanych tylko do jednego samolotu na raz (FCF i piloci testowi są godnymi uwagi wyjątkami), nie zastanawiałem się nad wieloma kwalifikacjami i sposobem ich wdrożenia. Ponieważ znam pilota, który 50 lat temu był zakwalifikowany do 25 różnych samolotów jednocześnie, myślę, że to pokazuje, w jaki sposób NIE chcemy używać wielokrotnego dziedziczenia.
Jay

20
Ponieważ jest mało prawdopodobne, aby jeden pilot latał jednocześnie więcej niż jednym samolotem, byłaby to dobra okazja do wdrożenia wzorca strategii. Pilot będzie miał kolekcję certyfikatów i wybierze właściwy w czasie wykonywania. Certyfikaty byłyby kodowane jako zachowania, które implementowałyby interfejs IFlyPlane, przy użyciu metod TakeOff, Land, Eject.
Michael Blackburn,

221

Myślę, że odpowiedź, której szukają, to podstawowa lub filozoficzna różnica OPPS.

Dziedziczenie klasy abstrakcyjnej jest używane, gdy klasa pochodna ma podstawowe właściwości i zachowanie klasy abstrakcyjnej. Rodzaj zachowania, które faktycznie określa klasę.

Z drugiej strony dziedziczenie interfejsu jest stosowane, gdy klasy dzielą zachowania peryferyjne, które niekoniecznie definiują klasę pochodną.

Na przykład Samochód i ciężarówka mają wiele podstawowych właściwości i zachowania abstrakcyjnej klasy samochodowej, ale mają też pewne zachowania peryferyjne, takie jak Generowanie układu wydechowego, które mają nawet klasy inne niż samochodowe, takie jak Wiertarki lub PowerGeneratory, i niekoniecznie definiują samochód lub ciężarówkę , więc Samochód, Ciężarówka, Wiertarka i PowerGenerator mogą dzielić ten sam interfejs IExhaust.


32
Myślę, że jeszcze lepszą analogią byłoby „usesFuel”, które pokazywałyby kontraktową naturę interfejsu.
Pureferret

@Pureferret, jeśli acceleratejest częścią podstawowego zachowania klasy abstrakcyjnej Automobile, to nie mogę powiedzieć, że acceleratepokazuje charakter umowy . czym jest natura kontraktowa? dlaczego to słowo contractpojawia się za każdym razem, gdy mówimy interface?
nadmierna wymiana

@overexchange, ponieważ zazwyczaj interfejs jest dokładnie tam, gdzie spotykają się dwie „powierzchnie”, ale słowo kontrakt oznacza, że ​​istnieje zgoda co do sposobu, w jaki spotykają się dwie „powierzchnie”. (Przynajmniej dla mnie) nie ma sensu, że wytwarzanie spalin jest czymś, co „zgadzasz się”. Ale to ma sens (ponownie dla mnie), że możesz zgodzić się na konieczność korzystania z paliwa.
Pureferret,

1
@Pureferret podniosłem kwerendy w linku do tego samego
overexchange

1
@Pureferret, jeśli interfacemusi zachowywać się na peryferiach, dlaczego public interface List<E> extends Collection<E> {}jest zaprojektowany do opisania podstawowych zachowań list? jest to w rzeczywistości sprzeczne z odpowiedzią prasun. Zarówno Collection<E>i List<E>interfejsy tutaj.
nadmierna wymiana

198

Krótko: Klasy abstrakcyjne są używane do modelowania hierarchii klas podobnie wyglądających klas (na przykład Zwierzę może być klasą abstrakcyjną, a Człowiek, Lew, Tygrys mogą być klasami pochodnymi)

I

Interfejs służy do komunikacji między 2 podobnymi / niepodobnymi klasami, które nie dbają o typ klasy implementującej interfejs (np. Wysokość może być własnością interfejsu i może być zaimplementowana przez człowieka, budynek, drzewo. Nie ma znaczenia, czy możesz jeść , możesz pływać, możesz umrzeć lub cokolwiek ... to ma znaczenie tylko to, co musisz mieć Wysokość (implementacja w twojej klasie).


7
Naprawdę podoba mi się ta odpowiedź, ponieważ czasami trudno jest odpowiedzieć na „co” różni się między rzeczami, patrząc na coś bardziej abstrakcyjnego, takiego jak zamiar , a nie tylko struktura (strukturalnie interfejs i klasa czysto abstrakcyjna są prawie takie same rzecz).
LostSalad,

Łatwo jest wyliczyć, co klasa abstrakcyjna kontra interfejs może zrobić w określonym języku, ale trudniej jest stworzyć abstrakcję, która nada sens i odpowiedzialność za sprzeciw, a to, co powiedziałeś, całkowicie wznowi stosowanie koncepcji 2 w OO. Dzięki!
Samuel

2
@dhananjay: Rozumiem, w jaki sposób Wysokość może być oddzielona od koncepcji klasy Zwierząt i innej klasy, ale co dokładnie rozumiesz przez „komunikację” między klasami? Po prostu określa wysokość dla swojej własnej klasy, prawda?
TTT

77

Istnieje kilka innych różnic -

Interfejsy nie mogą mieć żadnych konkretnych implementacji. Mogą to robić abstrakcyjne klasy podstawowe. Pozwala to na zapewnienie konkretnych wdrożeń. Może to pozwolić abstrakcyjnej klasie bazowej na zapewnienie bardziej rygorystycznego kontraktu, a interfejs naprawdę opisuje tylko sposób użycia klasy. (Abstrakcyjna klasa podstawowa może mieć nie-wirtualne elementy definiujące zachowanie, co daje większą kontrolę autorowi klasy podstawowej).

W klasie można zaimplementować więcej niż jeden interfejs. Klasa może wywodzić się tylko z jednej abstrakcyjnej klasy podstawowej. Pozwala to na hierarchię polimorficzną przy użyciu interfejsów, ale nie abstrakcyjnych klas bazowych. Pozwala to również na pseudo-wielokrotne dziedziczenie za pomocą interfejsów.

Abstrakcyjne klasy podstawowe można modyfikować w wersji 2 + bez psucia interfejsu API. Zmiany w interfejsach są przełomowymi zmianami.

[C # /. NET Specific] Interfejsy, w przeciwieństwie do abstrakcyjnych klas bazowych, mogą być stosowane do typów wartości (struktur). Struktury nie mogą dziedziczyć po abstrakcyjnych klasach podstawowych. Pozwala to na stosowanie kontraktów behawioralnych / wytycznych dotyczących użytkowania w odniesieniu do rodzajów wartości.


5
+1 za kluczowy punkt, że więcej niż jeden interfejs może być zaimplementowany w klasie.
cgp

To jedyna prawdziwa przewaga interfejsów nad abstrakcyjnymi klasami podstawowymi, IMO. W przeciwnym razie zgadzam się z wytycznymi projektowymi .NET, które mówią teraz, że „wolą abstrakcyjne klasy podstawowe od interfejsów”
Reed Copsey

Chociaż byłoby dobrze, gdybyś mógł dodać, że to także interfejsy można zastosować do dowolnej klasy.
cgp

1
@altCognito: Doszedłem do wniosku, że w pewnym stopniu poradzono sobie z drugim akapitem. Przypomniało mi to jednak, że interfejsy działają na typach wartości, więc to dodałem.
Reed Copsey

Dziękuję bardzo za ten dokładny opis. To jest naprawdę bardzo pomocne. Jestem tu nowy. Szkoda, że ​​nie możesz wybrać dwóch odpowiedzi jako „odpowiedzi”. Jedną z rzeczy, które mnie dezorientują, jest użycie abstrakcyjnej klasy „podstawowej”. Wszystkie klasy abstrakcyjne mają być klasą podstawową podklasy. Po co nazywać dodatkowe „podstawowe”?
Houman

68

Dziedzictwo
Zastanów się nad samochodem i autobusem. Są to dwa różne pojazdy. Ale nadal mają wspólne cechy, takie jak układ kierowniczy, hamulce, biegi, silnik itp.
Tak więc dzięki koncepcji dziedziczenia można to przedstawić następująco ...

public class Vehicle {
    private Driver driver;
    private Seat[] seatArray; //In java and most of the Object Oriented Programming(OOP) languages, square brackets are used to denote arrays(Collections).
    //You can define as many properties as you want here ...
}

Teraz rower ...

public class Bicycle extends Vehicle {
    //You define properties which are unique to bicycles here ...
    private Pedal pedal;
}

I samochód ...

public class Car extends Vehicle {
    private Engine engine;
    private Door[] doors;
}

To wszystko dotyczy dziedziczenia . Używamy ich do klasyfikowania obiektów w prostsze formy podstawowe i ich dzieci, jak widzieliśmy powyżej.

Klasy abstrakcyjne

Klasy abstrakcyjne są niekompletnymi obiektami. Aby to zrozumieć, jeszcze raz rozważmy analogię pojazdu.
Pojazd można prowadzić. Dobrze? Ale różne pojazdy są prowadzone na różne sposoby ... Na przykład nie można prowadzić samochodu tak, jak jeździ się rowerem.
Jak więc przedstawić funkcję jazdy pojazdu? Trudniej jest sprawdzić, jaki to pojazd, i prowadzić go własną funkcją; przy dodawaniu nowego typu pojazdu musiałbyś ciągle zmieniać klasę kierowcy.
Nadchodzi rola abstrakcyjnych klas i metod. Możesz zdefiniować metodę napędu jako abstrakcyjną, aby powiedzieć, że każde dziedziczące dziecko musi zaimplementować tę funkcję.
Jeśli więc zmodyfikujesz klasę pojazdu ...

//......Code of Vehicle Class
abstract public void drive();
//.....Code continues

Rower i samochód muszą również określać sposób prowadzenia pojazdu. W przeciwnym razie kod nie zostanie skompilowany i zostanie zgłoszony błąd.
W skrócie ... klasa abstrakcyjna jest częściowo niekompletną klasą z pewnymi niekompletnymi funkcjami, które dziedziczące dzieci muszą określić własne.

Interfejsy Interfejsy są całkowicie niekompletne. Nie mają żadnych właściwości. Wskazują tylko, że dziedziczące dzieci są w stanie coś zrobić ...
Załóżmy, że masz przy sobie różne rodzaje telefonów komórkowych. Każdy z nich ma inne sposoby wykonywania różnych funkcji; Np .: zadzwoń do osoby. Producent telefonu określa, jak to zrobić. Tutaj telefony komórkowe mogą wybrać numer - to znaczy, że można go wybrać. Przedstawmy to jako interfejs.

public interface Dialable {
    public void dial(Number n);
}

Tutaj twórca Dialable określa sposób wybierania numeru. Musisz tylko dać mu numer do wybrania.

// Makers define how exactly dialable work inside.

Dialable PHONE1 = new Dialable() {
    public void dial(Number n) {
        //Do the phone1's own way to dial a number
    }
}

Dialable PHONE2 = new Dialable() {
    public void dial(Number n) {
        //Do the phone2's own way to dial a number
    }
}


//Suppose there is a function written by someone else, which expects a Dialable
......
public static void main(String[] args) {
    Dialable myDialable = SomeLibrary.PHONE1;
    SomeOtherLibrary.doSomethingUsingADialable(myDialable);
}
.....

Używając interfejsów zamiast klas abstrakcyjnych, autor funkcji korzystającej z Dialable nie musi martwić się o swoje właściwości. Np .: Czy ma ekran dotykowy lub klawiaturę, Czy jest to telefon stacjonarny czy komórkowy? Musisz tylko wiedzieć, czy można go wybrać; dziedziczy (lub implementuje) interfejs Dialable.

A co ważniejsze , jeśli któregoś dnia zmienisz Dialable na inny

......
public static void main(String[] args) {
    Dialable myDialable = SomeLibrary.PHONE2; // <-- changed from PHONE1 to PHONE2
    SomeOtherLibrary.doSomethingUsingADialable(myDialable);
}
.....

Możesz być pewien, że kod nadal działa idealnie, ponieważ funkcja, która używa dialable, nie zależy (i nie może) od innych szczegółów niż te określone w interfejsie Dialable. Obie implementują interfejs Dialable i to jedyna rzecz, na której zależy jej funkcja.

Interfejsy są powszechnie używane przez programistów w celu zapewnienia interoperacyjności (używaj zamiennie) między obiektami, o ile mają one wspólną funkcję (podobnie jak możesz zmienić telefon stacjonarny lub komórkowy, o ile wystarczy wybrać numer). Krótko mówiąc, interfejsy są znacznie prostszą wersją klas abstrakcyjnych, bez żadnych właściwości.
Pamiętaj też, że możesz implementować (dziedziczyć) tyle interfejsów, ile chcesz, ale możesz rozszerzać (dziedziczyć) tylko jedną klasę nadrzędną.

Więcej informacji Klasy abstrakcyjne a interfejsy


Nie jest prawdą, że „Interfejsy nie mają żadnych właściwości”.
Bigeyes

@Bigeyes, Java nie zezwala na właściwości w interfejsach. Myślałem, że tak samo jest w innych językach. Czy możesz wyjaśnić więcej?
fz_salam

Mam na myśli C # /. Net. Zobacz przykład
Bigeyes

@Bigeyes dla C #, gdzie interfejsy mogą mieć właściwości, czy to nie przywraca problemu wielokrotnego dziedziczenia? Co dzieje się, gdy klasa korzysta z wielu interfejsów, które zdefiniowały tę samą właściwość? Po prostu ciekawe dzięki
stackPusher

@happycoder: re: "Używając interfejsów zamiast klas abstrakcyjnych, nie musisz się martwić o jego właściwości. Np .: Czy ma ekran dotykowy lub klawiaturę, Czy jest to stacjonarny telefon stacjonarny czy telefon komórkowy. Musisz tylko wiedzieć, czy można go wybierać; czy dziedziczy (lub implementuje) interfejs wybierania numeru. ” - czy możesz to pokazać na przykładzie kodu, również nie widziałem, jak zostanie odziedziczony ...
TTT

45

Jeśli uważasz, że javajęzyk OOP odpowiada na to pytanie, wydanie Java 8 powoduje, że niektóre z powyższych odpowiedzi są przestarzałe. Teraz interfejs Java może mieć domyślne metody z konkretną implementacją.

Oracle strona dostarcza kluczowych różnic pomiędzy interfacei abstractklasę.

Rozważ użycie klas abstrakcyjnych, jeśli:

  1. Chcesz dzielić kod między kilkoma blisko spokrewnionymi klasami.
  2. Oczekujesz, że klasy, które rozszerzają twoją klasę abstrakcyjną, mają wiele wspólnych metod lub pól lub wymagają modyfikatorów dostępu innych niż publiczne (takie jak chronione i prywatne).
  3. Chcesz zadeklarować pola niestatyczne lub nie-końcowe.

Rozważ użycie interfejsów, jeśli:

  1. Oczekujesz, że niepowiązane klasy zaimplementują Twój interfejs. Na przykład wiele niepowiązanych obiektów może implementować Serializableinterfejs.
  2. Chcesz określić zachowanie określonego typu danych, ale nie przejmuj się tym, kto wdraża to zachowanie.
  3. Chcesz skorzystać z wielokrotnego dziedziczenia typu.

Mówiąc najprościej, chciałbym użyć

interfejs: Aby wdrożyć umowę przez wiele niepowiązanych obiektów

Klasa abstrakcyjna: Aby zaimplementować to samo lub inne zachowanie wśród wielu powiązanych obiektów

Spójrz na przykład kodu, aby zrozumieć wszystko w jasny sposób: Jak powinienem wyjaśnić różnicę między interfejsem a klasą abstrakcyjną?


33

Ankieterzy szczekają dziwne drzewo. W przypadku języków takich jak C # i Java jest różnica, ale w innych językach, takich jak C ++, nie ma. Teoria OO nie rozróżnia tych dwóch, a jedynie składnię języka.

Klasa abstrakcyjna to klasa z implementacją i interfejsem (czysto wirtualne metody), która zostanie odziedziczona. Interfejsy zasadniczo nie mają żadnej implementacji, a jedynie czysto wirtualne funkcje.

W języku C # lub Java klasa abstrakcyjna bez żadnej implementacji różni się od interfejsu tylko składnią używaną do dziedziczenia z niego oraz faktem, że można dziedziczyć tylko z jednej.


Zadano mi to samo pytanie tydzień temu, nie mam doświadczenia z Javą, ale od jakiegoś czasu pracuję z C ++. Ankieter nie określił języków przed zadaniem pytania, więc właśnie wyjaśniłem, że interfejsy w tym przypadku były klasami abstrakcyjnymi bez żadnego stanu lub implementacji jakiegokolwiek rodzaju. Zgadzam się, że to dziwne pytanie.
dacabdi

31

Wdrażając interfejsy, uzyskujesz kompozycję (relacje „has-a”) zamiast dziedziczenia (relacje „is-a”). Jest to ważna zasada, o której należy pamiętać, gdy chodzi o takie rzeczy, jak wzorce projektowe, w których należy użyć interfejsów, aby uzyskać kompozycję zachowań zamiast dziedziczenia.


17
Interfejsy osiągają, IMO, bardziej relację „działa jak”. Enkapsulacja osiąga kompozycję lepiej niż interfejs.
Reed Copsey

12
Nie sądzę, żeby implementacja interfejsów mogła zostać ułożona.
Pavan Dittakavi,

Ponadto interfejs częściej służy do opisywania „możliwości”, takich jak IDisposable. Kiedyś dzielono funkcjonalność między klasami, aby te klasy „mogły” coś zrobić. Więcej przykładów IFlyable może być wdrożony przez ptaka i samolot. Ale Ptak może wywodzić się z klasy Creature, gdzie samolot pochodzi od AirCraft.
Peter.Wang

26

wyjaśnię szczegóły głębokości interfejsu i klasy abstrakcyjnej. jeśli znasz ogólne informacje o interfejsie i klasie abstrakcyjnej, wtedy przyjdzie ci do głowy pierwsze pytanie, kiedy powinniśmy użyć interfejsu, a kiedy powinniśmy użyć klasy abstrakcyjnej. Więc sprawdź poniżej wyjaśnienie klasy Interface i Abstract.

  1. Kiedy powinniśmy używać interfejsu?

    jeśli nie wiesz o implementacji, tylko mamy specyfikację wymagań, wówczas korzystamy z interfejsu

  2. Kiedy powinniśmy używać klasy abstrakcyjnej?

    jeśli znasz implementację, ale nie całkowicie (częściowo implementację), to wybieramy klasę Abstract.

    Berło

    każda metoda domyślnie public abstract oznacza, że ​​interfejs jest w 100% czystym abstraktem.

    Abstrakcyjny

    może mieć metodę Konkretną i Metodę Abstrakcyjną, czyli Metodę Konkretną, która ma implementację w klasie Abstrakcyjnej, Klasa abstrakcyjna to klasa, która jest zadeklarowana jako abstrakcyjna - może zawierać metody abstrakcyjne lub nie.

    Berło

    Nie możemy zadeklarować interfejsu jako prywatny, chroniony

    P: Dlaczego nie ogłaszamy, że interfejs jest prywatny i chroniony?

    Ponieważ domyślnie metoda interfejsu jest publicznie abstrakcyjna, dlatego też nie ogłaszamy, że interfejs jest prywatny i chroniony.

    Metoda interfejsu
    również nie możemy zadeklarować interfejsu jako prywatny, chroniony, końcowy, statyczny, zsynchronizowany, natywny .....

    Podam powód: dlaczego nie deklarujemy metody zsynchronizowanej, ponieważ nie możemy stworzyć obiektu interfejsu, a synchronizacja jest pracą na obiekcie, a więc powód, dla którego nie deklarujemy metody zsynchronizowanej Koncepcja przejściowa również nie ma zastosowania, ponieważ praca przejściowa z synchronizacją.

    Abstrakcyjny

    chętnie używamy z publicznymi, prywatnymi statycznymi statycznymi .... oznacza, że ​​żadne ograniczenia nie mają zastosowania w sposób abstrakcyjny.

    Berło

    Zmienne są zadeklarowane w interfejsie jako domyślny publiczny statyczny finał, dlatego też nie jesteśmy zadeklarowani jako zmienni prywatni, chronieni.

    Zmienny modyfikator nie ma również zastosowania w interfejsie, ponieważ zmienna interfejsu jest domyślnie publiczną statyczną zmienną końcową i końcową, nie można zmienić wartości po przypisaniu wartości do zmiennej, a po zadeklarowaniu zmiennej w interfejsie należy ją przypisać.

    Zmienna zmienna ciągle się zmienia, więc jest przeciwna. do końca dlatego nie używamy zmiennych zmiennych w interfejsie.

    Abstrakcyjny

    Zmienna abstrakcyjna nie ma potrzeby deklarowania publicznego statycznego końcowego

mam nadzieję, że ten artykuł będzie przydatny.


4
Nie zgadzam się z tym punktem: Abstract class must have at lease one abstract method.Możliwe jest posiadanie klasy Abstract bez metody Abstract, o ile ją zaimplementujesz. ODNIESIENIE: An abstract class is a class that is declared abstract—it may or may not include abstract methods.ŹRÓDŁO ODNIESIENIA: docs.oracle.com/javase/tutorial/java/IandI/abstract.html
Devner

Mówisz o szczegółach technicznych i implementacji, nie odpowiadasz na pytanie w kategoriach ogólnego OOP
Billal Begueradj

26

Mówiąc koncepcyjnie, utrzymanie implementacji specyficznych dla języka, zasad, korzyści i osiągnięcie dowolnego celu programowania przy użyciu dowolnego lub obu, może lub nie może mieć kodu / danych / właściwości, bla bla, pojedynczego lub wielokrotnego dziedziczenia, wszystko na boku

1- Klasa abstrakcyjna (lub czysto abstrakcyjna) Klasa służy do implementacji hierarchii. Jeśli obiekty biznesowe wyglądają nieco strukturalnie podobnie, reprezentując jedynie relację rodzic-dziecko (hierarchia), wówczas zostaną użyte klasy dziedziczenia / abstrakcyjne. Jeśli twój model biznesowy nie ma hierarchii, nie należy stosować dziedziczenia (tutaj nie mówię o logice programowania, np. Niektóre wzorce projektowe wymagają dziedziczenia). Koncepcyjnie klasa abstrakcyjna to metoda implementacji hierarchii modelu biznesowego w OOP, nie ma ona nic wspólnego z interfejsami, w rzeczywistości porównywanie klasy abstrakcyjnej z interfejsem jest bez znaczenia, ponieważ obie są koncepcyjnie całkowicie różnymi rzeczami, w wywiadach jest proszony o sprawdzenie pojęć, ponieważ oba wydają się zapewniać nieco taką samą funkcjonalność, jeśli chodzi o implementację, a my programiści zwykle kładziemy większy nacisk na kodowanie. [Pamiętaj również, że abstrakcja różni się od klasy abstrakcyjnej].

2 - Interfejs to umowa, pełna funkcjonalność biznesowa reprezentowana przez jeden lub więcej zestaw funkcji. Dlatego jest zaimplementowany, a nie odziedziczony. Obiekt biznesowy (część hierarchii lub nie) może mieć dowolną liczbę pełnych funkcji biznesowych. Nie ma to nic wspólnego z klasami abstrakcyjnymi, ale ogólnie z dziedziczeniem. Na przykład człowiek może URUCHOMIĆ, słoń może URUCHOMIĆ, ptak może URUCHOMIĆ, i tak dalej, wszystkie te obiekty o różnej hierarchii implementują interfejs RUN lub interfejs EAT lub SPEAK. Nie wchodź w implementację, ponieważ możesz zaimplementować ją jako posiadającą klasy abstrakcyjne dla każdego typu implementującego te interfejsy. Obiekt dowolnej hierarchii może mieć funkcjonalność (interfejs), która nie ma nic wspólnego z jego hierarchią.

Uważam, że interfejsy nie zostały wymyślone w celu osiągnięcia wielu spadków lub ujawnienia publicznego zachowania, podobnie czyste klasy abstrakcyjne nie zastępują interfejsów, ale interfejs jest funkcją, którą może wykonywać obiekt (poprzez funkcje tego interfejsu), a klasa abstrakcyjna reprezentuje element nadrzędny hierarchii w celu utworzenia elementów podrzędnych o podstawowej strukturze (właściwość + funkcjonalność) elementu nadrzędnego

Kiedy zostaniesz zapytany o różnicę, tak naprawdę jest to różnica konceptualna, a nie różnica w implementacji specyficznej dla języka, chyba że zostaniesz wyraźnie zapytany.

Uważam, że obaj ankieterzy oczekiwali jednej linii prostej różnicy między tymi dwoma, a kiedy ci się nie udało, próbowali doprowadzić cię do tej różnicy, wdrażając JEDEN jako INNY

Co by było, gdybyś miał klasę abstrakcyjną z tylko abstrakcyjnymi metodami?


To całkiem dobrze podsumowuje odpowiedź na to pytanie.
pranavn

zaimplementowana funkcjonalność vs rozbudowana struktura, fajnie!
harshvchawla

21

Dla .Net,

Twoja odpowiedź na Drugi ankieter jest również odpowiedzią na pierwszą ... Klasy abstrakcyjne mogą mieć implementację, ORAZ, interfejsy nie mogą ...

EDYCJA: Z drugiej strony, nie użyłbym nawet frazy „podklasa” (lub „dziedziczenie”) do opisania klas, które są „zdefiniowane do implementacji” interfejsu. Dla mnie interfejs jest definicją kontraktu, z którym klasa musi się zgadzać, jeśli została zdefiniowana w celu „implementacji” tego interfejsu. Niczego nie dziedziczy ... Musisz sam wszystko dodać.


2
Tak! Stan! To właśnie miał na myśli drugi ankieter ze swoim dziwnym sposobem mówienia „zmienna publiczna” w interfejsie. Boże! Klasy abstrakcyjne mogą mieć stan, interfejsy nie! I tak, wszyscy inni zgadzają się również na temat różnic między sposobami dziedziczenia, o których zapomniałem wspomnieć, ale zorientowałem się już później. :) Dziękuję wszystkim!
Houman

4
Więcej niż tylko stan .... Klasy abstrakcyjne mogą mieć WDROŻENIE. tzn. mogą mieć metody z kodem, który faktycznie działa i robi coś, co zostaje odziedziczone i wykonane przez instancje klas podstawowych ... Nie dotyczy to interfejsów
Charles Bretana

Co więcej, w jednym sensie można tworzyć instancje klas abstrakcyjnych, muszą one być tworzone przy użyciu definicji klas pochodnych, a nie bezpośrednio. Ale zmienne stanu zdefiniowane w klasie abstrakcyjnej są tworzone w obiekcie utworzonym przez odświeżenie instancji klasy pochodnej. Ta instancja JEST instancją klasy abstrakcyjnej, a także instancją klasy pochodnej - w końcu pochodzi od niej. Nie dotyczy to interfejsu.
Charles Bretana,

Kiedy odnajdujesz instancję klasy zdefiniowanej do implementacji interfejsu, nie jest to „instancja” tego interfejsu, cała składnia powoduje, że kompilator sprawdza kod dla klasy i zapewnia, że ​​każde zachowanie (metoda, właściwość , event, eventHandler itp.), który jest zdefiniowany przez interfejs, został zaimplementowany w kodzie klasy.
Charles Bretana,

20

Interfejs : należy używać, jeśli chcesz sugerować regułę dla komponentów, które mogą, ale nie muszą być ze sobą powiązane

Plusy:

  1. Umożliwia wielokrotne dziedziczenie
  2. Zapewnia abstrakcję, nie ujawniając, jakiego rodzaju obiekt jest używany w kontekście
  3. zapewnia spójność poprzez konkretny podpis umowy

Cons:

  1. Musi wdrożyć wszystkie zdefiniowane umowy
  2. Nie może mieć zmiennych ani delegatów
  3. Raz zdefiniowanego nie można zmienić bez rozbicia wszystkich klas

Klasa abstrakcyjna : należy stosować tam, gdzie chcesz mieć podstawowe lub domyślne zachowanie lub implementację powiązanych ze sobą komponentów

Plusy:

  1. Szybszy niż interfejs
  2. Ma elastyczność we wdrażaniu (możesz go w pełni lub częściowo wdrożyć)
  3. Można go łatwo zmienić bez przerywania pochodnych klas

Cons:

  1. Nie można utworzyć wystąpienia
  2. Nie obsługuje wielokrotnego dziedziczenia

Definiuj szybciej. Czy to jest znaczące? Co to w ogóle oznacza? opcode do wywoływania funkcji w klasie abstrakcyjnej jest szybszy niż opcode do wywoływania funkcji w interfejsie?
denis631

@ denis631 klasy abstrakcyjne są nieco szybsze niż interfejs ze względu na wyszukiwanie i wywołanie jest zaangażowane w metodę interfejsu. przeczytaj ten coderanch.com/t/503450/java/abstract-class-faster-interface
webmaster bourax

17

Myślę, że nie podobała im się twoja odpowiedź, ponieważ podałeś różnice techniczne zamiast projektowych. To pytanie jest dla mnie jak troll. W rzeczywistości interfejsy i klasy abstrakcyjne mają zupełnie inny charakter, więc nie można ich tak naprawdę porównać. Dam ci moją wizję roli interfejsu i roli klasy abstrakcyjnej.

Interfejs: służy do zapewnienia kontraktu i niskiego sprzężenia między klasami w celu uzyskania łatwiejszej w utrzymaniu, skalowalnej i testowalnej aplikacji.

Klasa abstrakcyjna: służy tylko do faktoryzacji kodu między klasami o tej samej odpowiedzialności. Zauważ, że jest to główny powód, dla którego wielokrotne dziedziczenie jest złą rzeczą w OOP, ponieważ klasa nie powinna obsługiwać wielu obowiązków ( zamiast tego użyj kompozycji ).

Interfejsy mają więc prawdziwą rolę architektoniczną, podczas gdy klasy abstrakcyjne są prawie tylko szczegółem implementacji (jeśli używasz go prawidłowo).


13
After all that, the interviewer came up with the question "What if you had an 
Abstract class with only abstract methods? How would that be different
from an interface?" 

Dokumenty wyraźnie mówią, że jeśli klasa abstrakcyjna zawiera tylko deklaracje metody abstrakcyjnej, powinna zostać zadeklarowana jako interfejs.

An another interviewer asked me what if you had a Public variable inside
the interface, how would that be different than in Abstract Class?

Zmienne w interfejsach są domyślnie publiczne statyczne i końcowe. Pytanie może zostać sformułowane tak, jakby wszystkie zmienne w klasie abstrakcyjnej były publiczne? Cóż, nadal mogą być niestatyczne i nieostateczne, w przeciwieństwie do zmiennych w interfejsach.

Na koniec dodałbym jeszcze jeden punkt do tych wymienionych powyżej - klasy abstrakcyjne są nadal klasami i mieszczą się w jednym drzewie dziedziczenia, podczas gdy interfejsy mogą występować w wielu elementach dziedziczenia.


13
  1. Berło:
    • Nie implementujemy (ani nie definiujemy) metod, robimy to w klasach pochodnych.
    • Nie deklarujemy zmiennych składowych w interfejsach.
    • Interfejsy wyrażają związek HAS-A. Oznacza to, że są maską przedmiotów.
  2. Klasa abstrakcyjna:
    • Możemy deklarować i definiować metody w klasie abstrakcyjnej.
    • Ukrywamy jego konstruktorów. Oznacza to, że nie utworzono z niego bezpośrednio obiektu.
    • Klasa abstrakcyjna może przechowywać zmienne składowe.
    • Klasy pochodne dziedziczą do klasy abstrakcyjnej, co oznacza, że ​​obiekty z klas pochodnych nie są maskowane, dziedziczą do klasy abstrakcyjnej. Relacja w tym przypadku to IS-A.

To jest moja opinia.


12

Skopiowane z CLR przez C # przez Jeffrey Richter ...

Często słyszę pytanie: „Czy powinienem zaprojektować typ podstawowy czy interfejs?” Odpowiedź nie zawsze jest jednoznaczna.

Oto kilka wskazówek, które mogą ci pomóc:

■■ Relacja IS-A vs. CAN-DO Typ może dziedziczyć tylko jedną implementację. Jeśli typ pochodny nie może twierdzić o związku IS-A z typem podstawowym, nie należy używać typu podstawowego; użyj interfejsu. Interfejsy sugerują związek CAN-DO. Jeśli wydaje się, że funkcjonalność CAN-DO należy do różnych typów obiektów, użyj interfejsu. Na przykład typ może konwertować instancje samego siebie na inny typ (IConvertible), typ może serializować instancję samego siebie (ISerializable) itp. Uwaga: typy wartości muszą pochodzić z System.ValueType, a zatem nie można ich wyprowadzić z dowolnej klasy bazowej. W takim przypadku musisz użyć relacji CAN-DO i zdefiniować interfejs.

■■ Łatwość użycia Generalnie łatwiej jest programistom zdefiniować nowy typ na podstawie typu podstawowego niż wdrożyć wszystkie metody interfejsu. Typ podstawowy może zapewniać wiele funkcji, więc typ pochodny prawdopodobnie wymaga jedynie stosunkowo niewielkich modyfikacji swojego zachowania. Jeśli podasz interfejs, nowy typ musi implementować wszystkie elementy.

■■ Spójne wdrożenie Bez względu na to, jak dobrze dokumentowana jest umowa dotycząca interfejsu, jest bardzo mało prawdopodobne, aby wszyscy w 100% poprawnie zrealizowali umowę. W rzeczywistości COM cierpi z powodu tego bardzo problemu, dlatego niektóre obiekty COM działają poprawnie tylko z Microsoft Word lub Windows Internet Explorer. Zapewniając typ podstawowy z dobrą domyślną implementacją, zaczynasz od użycia typu, który działa i jest dobrze przetestowany; możesz następnie modyfikować części wymagające modyfikacji.

■■ Wersjonowanie Jeśli dodasz metodę do typu podstawowego, typ pochodny odziedziczy nową metodę, zaczniesz od użycia typu, który działa, a kod źródłowy użytkownika nie musi nawet zostać ponownie skompilowany. Dodanie nowego elementu do interfejsu zmusza spadkobiercę interfejsu do zmiany kodu źródłowego i ponownej kompilacji.


1
@AbdullahShoaib jest-i każdy może zrobić, ale nie może zrobić, tutaj jest różnica. to jest podstawowy powód, dla którego potrzebujemy interfejsu. możliwe będzie abstract classrównież zachowanie.
nadmierna wymiana

10

Interfejs definiuje umowę dotyczącą usługi lub zestawu usług. Zapewniają one polimorfizm w sposób poziomy, ponieważ dwie całkowicie niezwiązane klasy mogą implementować ten sam interfejs, ale mogą być używane zamiennie jako parametr typu interfejsu, który implementują, ponieważ obie klasy obiecały spełnić zestaw usług zdefiniowanych przez interfejs. Interfejsy nie zawierają szczegółów implementacji.

Klasa abstrakcyjna definiuje strukturę podstawową dla swoich podklas i opcjonalnie częściową implementację. Klasy abstrakcyjne zapewniają polimorfizm w sposób pionowy, ale kierunkowy, w ten sposób, że każda klasa dziedzicząca klasę abstrakcyjną może być traktowana jako instancja tej klasy abstrakcyjnej, ale nie na odwrót. Klasy abstrakcyjne mogą i często zawierają szczegóły implementacji, ale nie mogą być tworzone samodzielnie - tylko ich podklasy mogą być „odnawiane”.

C # pozwala również na dziedziczenie interfejsu.


1
Użycie terminów poziomej i pionowej bardzo wyraźnie wyobrażało różnicę.
Infinity

10

Większość odpowiedzi skupia się na technicznej różnicy między klasą abstrakcyjną a interfejsem, ale ponieważ technicznie interfejs jest zasadniczo rodzajem abstrakcyjnej klasy (jedna bez żadnych danych ani implementacji), myślę, że różnica koncepcyjna jest o wiele bardziej interesująca i może to być to, co ankieterzy szukają.

Interfejs jest umowa . Określa: „w ten sposób będziemy ze sobą rozmawiać”. Nie może mieć żadnej implementacji, ponieważ nie powinna mieć żadnej implementacji. To umowa. To jest jak .hpliki nagłówkowe w C.

Streszczenie Klasa jest niepełna realizacja . Klasa może, ale nie musi, implementować interfejs, a klasa abstrakcyjna nie musi go całkowicie implementować. Klasa abstrakcyjna bez żadnej implementacji jest bezużyteczna, ale całkowicie legalna.

Zasadniczo każda klasa, abstrakcyjna lub nie, dotyczy tego, czym jest , podczas gdy interfejs dotyczy tego, jak z niej korzystasz . Na przykład: Animalmoże być klasą abstrakcyjną implementującą niektóre podstawowe funkcje metaboliczne i określającą abstrakcyjne metody oddychania i poruszania się bez podania implementacji, ponieważ nie ma pojęcia, czy powinien oddychać przez skrzela lub płuca i czy lata, pływa, chodzi lub czołga się. Mount, z drugiej strony, może być interfejsem, który określa, że ​​możesz jeździć na zwierzęciu, nie wiedząc, jakie to zwierzę (lub czy w ogóle jest zwierzęciem!).

Fakt, że za kulisami interfejs jest w zasadzie klasą abstrakcyjną z jedynie abstrakcyjnymi metodami, nie ma znaczenia. Pod względem koncepcyjnym pełnią one zupełnie inne role.


10

Jak zapewne posiadasz wiedzę teoretyczną od ekspertów, nie powtarzam zbyt wielu słów tutaj, pozwól mi wyjaśnić na prostym przykładzie, gdzie możemy użyć / nie możemy użyć Interfacei Abstract class.

Zastanów się, czy projektujesz aplikację, aby wyświetlić listę wszystkich funkcji samochodów. W różnych punktach konieczne jest wspólne dziedziczenie, ponieważ niektóre właściwości, takie jak DigitalFuelMeter, klimatyzacja, regulacja siedzeń itp. Są wspólne dla wszystkich samochodów. Podobnie, potrzebujemy dziedziczenia tylko dla niektórych klas, ponieważ niektóre właściwości, takie jak układ hamulcowy (ABS, EBD) mają zastosowanie tylko do niektórych samochodów.

Poniższa klasa działa jako klasa bazowa dla wszystkich samochodów:

public class Cars
{
    public string DigitalFuelMeter()
    {
        return "I have DigitalFuelMeter";
    }

    public string AirCondition()
    {
        return "I have AC";
    }

    public string SeatAdjust()
    {
        return "I can Adjust seat";
    }
}

Rozważ, że mamy osobną klasę dla każdego samochodu.

public class Alto : Cars
{
    // Have all the features of Car class    
}

public class Verna : Cars
{
    // Have all the features of Car class + Car need to inherit ABS as the Braking technology feature which is not in Cars        
}

public class Cruze : Cars
{
    // Have all the features of Car class + Car need to inherit EBD as the Braking technology feature which is not in Cars        
}

Rozważmy, że potrzebujemy metody dziedziczenia technologii hamowania dla samochodów Verna i Cruze (nie dotyczy Alto). Chociaż oba wykorzystują technologię hamowania, „technologia” jest inna. Tworzymy więc klasę abstrakcyjną, w której metoda zostanie zadeklarowana jako Abstrakcyjna i powinna zostać zaimplementowana w jej klasach potomnych.

public abstract class Brake
{
    public abstract string GetBrakeTechnology();
}

Teraz staramy się odziedziczyć tę abstrakcyjną klasę, a typ układu hamulcowego jest zaimplementowany w Verna i Cruze:

public class Verna : Cars,Brake
{
    public override string GetBrakeTechnology()
    {
        return "I use ABS system for braking";
    }       
}

public class Cruze : Cars,Brake
{
    public override string GetBrakeTechnology()
    {
       return "I use EBD system for braking";
    }         
}

Widzisz problem w dwóch powyższych klasach? Dziedziczą po wielu klasach, na które C # .Net nie pozwala, mimo że metoda jest zaimplementowana w elementach potomnych. Nadchodzi potrzeba interfejsu.

interface IBrakeTechnology
{
    string GetBrakeTechnology();
}

A wdrożenie podano poniżej:

public class Verna : Cars, IBrakeTechnology
{
    public string GetBrakeTechnology()
    {
        return "I use ABS system for braking";
    }
}

public class Cruze : Cars, IBrakeTechnology
{
   public string GetBrakeTechnology()
   {
       return "I use EBD system for braking";
   }        
}

Teraz Verna i Cruze mogą osiągnąć wielokrotne dziedzictwo dzięki własnym technologiom hamowania za pomocą interfejsu.


4
To jedno z najlepszych wyjaśnień ze względu na przykłady.
Adam Mendoza,

2
Ma to dla mnie sens bez dręczenia mózgu. Właśnie próbowałem wymyślić przykład samochodu dla moich uczniów. Dziękujemy za poświęcenie czasu na złożenie tego w całość.
tazboy

9

Interfejsy są lekkim sposobem wymuszenia określonego zachowania. To jeden ze sposobów myślenia.


8

Te odpowiedzi są za długie.

  • Interfejsy służą do definiowania zachowań.

  • Klasy abstrakcyjne służą do definiowania samej rzeczy, w tym jej zachowań. Dlatego czasami tworzymy klasę abstrakcyjną z pewnymi dodatkowymi właściwościami dziedziczącymi interfejs.

Wyjaśnia to również, dlaczego Java obsługuje tylko pojedyncze dziedziczenie dla klas, ale nie nakłada żadnych ograniczeń na interfejsy. Ponieważ konkretny obiekt nie może być różnymi rzeczami, ale może mieć różne zachowania.


7

1) Interfejs może być postrzegany jako czysta klasa abstrakcyjna, jest taki sam, ale mimo to nie jest taki sam, jeśli chodzi o implementację interfejsu i dziedziczenie po klasie abstrakcyjnej. Kiedy dziedziczysz po tej czystej abstrakcyjnej klasie, definiujesz hierarchię -> dziedziczenie, jeśli zaimplementujesz interfejs, nie jesteś i możesz zaimplementować tyle interfejsów, ile chcesz, ale możesz dziedziczyć tylko z jednej klasy.

2) Możesz zdefiniować właściwość w interfejsie, więc klasa implementująca ten interfejs musi mieć tę właściwość.

Na przykład:

  public interface IVariable
  {
      string name {get; set;}
  }

Klasa implementująca ten interfejs musi mieć taką właściwość.


7

Chociaż to pytanie jest dość stare, chciałbym dodać jeszcze jedną rzecz na korzyść interfejsów:

Interfejsy można wstrzykiwać za pomocą dowolnego narzędzia wstrzykiwania zależności, w którym wstrzykiwanie klasy abstrakcyjnej jest obsługiwane przez bardzo nieliczne.


1
Uważam, że masz na myśli, że narzędzie DI może wstrzyknąć klasę, która implementuje interfejs. Niektóre takie narzędzia mogą również wstrzykiwać klasy wywodzące się z klasy abstrakcyjnej, czy uważasz, że jest to niemożliwe?
John Saunders

6

Z innej mojej odpowiedzi , głównie dotyczącej tego, kiedy użyć jednej kontra drugiej:

Z mojego doświadczenia wynika, że ​​interfejsów najlepiej używać, gdy masz kilka klas, z których każda musi odpowiadać tej samej metodzie lub metodom, aby mogły być używane zamiennie przez inny kod, który zostanie napisany przeciwko wspólnemu interfejsowi tych klas. Najlepszym zastosowaniem interfejsu jest, gdy protokół jest ważny, ale podstawowa logika może być inna dla każdej klasy. Jeśli w innym przypadku logika byłaby duplikowana, rozważ zamiast tego klasy abstrakcyjne lub standardowe dziedziczenie klas.


6

Typy interfejsów a abstrakcyjne klasy podstawowe

Na podstawie książki Pro C # 5.0 i .NET 4.5 Framework .

Typ interfejsu może wydawać się bardzo podobny do abstrakcyjnej klasy bazowej. Przypomnij sobie, że gdy klasa jest oznaczona jako abstrakcyjna, może zdefiniować dowolną liczbę elementów abstrakcyjnych, aby zapewnić interfejs polimorficzny dla wszystkich typów pochodnych. Jednak nawet gdy klasa definiuje zestaw elementów abstrakcyjnych, może również dowolnie definiować dowolną liczbę konstruktorów, dane pól, elementy nieabstrakcyjne (z implementacją) i tak dalej. Z drugiej strony interfejsy zawierają tylko abstrakcyjne definicje elementów. Interfejs polimorficzny ustanowiony przez abstrakcyjną klasę nadrzędną podlega jednemu głównemu ograniczeniu, ponieważ tylko typy pochodne obsługują elementy zdefiniowane przez abstrakcyjnego rodzica. Jednak w większych systemach oprogramowania bardzo często rozwija się wiele hierarchii klas, które nie mają wspólnego elementu nadrzędnego poza System.Object. Biorąc pod uwagę, że elementy abstrakcyjne w abstrakcyjnej klasie bazowej dotyczą tylko typów pochodnych, nie mamy możliwości skonfigurowania typów w różnych hierarchiach w celu obsługi tego samego interfejsu polimorficznego. Jako przykład załóżmy, że zdefiniowałeś następującą klasę abstrakcyjną:

public abstract class CloneableType
{
// Only derived types can support this
// "polymorphic interface." Classes in other
// hierarchies have no access to this abstract
// member.
   public abstract object Clone();
}

Biorąc pod uwagę tę definicję, tylko członkowie, którzy rozszerzają CloneableType, mogą obsługiwać metodę Clone (). Jeśli utworzysz nowy zestaw klas, które nie rozszerzają tej klasy podstawowej, nie możesz uzyskać interfejsu polimorficznego. Możesz także pamiętać, że C # nie obsługuje wielokrotnego dziedziczenia klas. Dlatego jeśli chcesz utworzyć MiniVan, który jest samochodem i CloneableType, nie możesz tego zrobić:

// Nope! Multiple inheritance is not possible in C#
// for classes.
public class MiniVan : Car, CloneableType
{
}

Jak można się domyślić, na ratunek przychodzą typy interfejsów. Po zdefiniowaniu interfejsu można go wdrożyć w dowolnej klasie lub strukturze, w dowolnej hierarchii, w dowolnej przestrzeni nazw lub w dowolnym zestawie (napisanym w dowolnym języku programowania .NET). Jak widać, interfejsy są wysoce polimorficzne. Rozważ standardowy interfejs .NET o nazwie ICloneable, zdefiniowany w przestrzeni nazw System. Ten interfejs definiuje pojedynczą metodę o nazwie Clone ():

public interface ICloneable
{
object Clone();
}

6

Odpowiedź na drugie pytanie: publiczmienna zdefiniowana w interfacejest static finaldomyślnie, podczas gdy publiczmienna w abstractklasie jest zmienną instancji.


6

Z pewnością ważne jest, aby zrozumieć zachowanie interfejsu i klasy abstrakcyjnej w OOP (oraz sposób, w jaki języki je obsługują), ale myślę, że ważne jest również, aby zrozumieć, co dokładnie oznacza każdy termin. Czy możesz sobie wyobrazić, że ifpolecenie nie działa dokładnie w znaczeniu tego terminu? Ponadto w rzeczywistości niektóre języki zmniejszają, a nawet bardziej, różnice między interfejsem a streszczeniem ... jeśli przypadkiem któregoś dnia oba terminy działają prawie identycznie, przynajmniej możesz sam określić, gdzie (i dlaczego) którykolwiek z nich powinien być używany do.

Jeśli przeczytasz niektóre słowniki i inne czcionki, możesz znaleźć inne znaczenia dla tego samego terminu, ale mając pewne wspólne definicje. Myślę, że te dwa znaczenia, które znalazłem na tej stronie, są naprawdę, bardzo dobre i odpowiednie.

Berło:

Rzecz lub okoliczność, która umożliwia skuteczne koordynowanie oddzielnych, a czasem niekompatybilnych elementów.

Abstrakcyjny:

Coś, co skupia w sobie podstawowe cechy czegokolwiek bardziej obszernego, bardziej ogólnego lub kilku rzeczy; istota.

Przykład:

Kupiłeś samochód, który potrzebuje paliwa.

wprowadź opis zdjęcia tutaj

Twój model samochodu jest XYZgatunkiem ABC, więc jest konkretnym samochodem, konkretnym przykładem samochodu. Samochód nie jest prawdziwym przedmiotem. W rzeczywistości jest to abstrakcyjny zestaw standardów (cech) służących do tworzenia określonego obiektu. Krótko mówiąc, Car jest klasą abstrakcyjną , jest „czymś, co skupia w sobie podstawowe cechy czegoś szerszego lub bardziej ogólnego” .

Jedyne paliwo, które odpowiada specyfikacji samochodu, powinno być użyte do napełnienia zbiornika samochodu. W rzeczywistości nic nie ogranicza Cię do wlewania paliwa, ale silnik będzie działał poprawnie tylko z określonym paliwem, więc lepiej jest przestrzegać jego wymagań. Wymagania mówią, że akceptuje, podobnie jak inne samochody tego samego gatunku ABC, standardowy zestaw paliwa.

W widoku obiektowym paliwo dla gatunku ABCnie powinno być deklarowane jako klasa, ponieważ nie ma konkretnego paliwa dla określonego gatunku samochodu. Chociaż twój samochód może zaakceptować paliwo abstrakcyjne lub Fuel Vehicle, musisz pamiętać, że tylko niektóre z istniejących paliw samochodowych spełniają specyfikację, które spełniają wymagania zawarte w instrukcji obsługi samochodu. Krótko mówiąc, powinni wdrożyć interfejs ABCGenreFuel , który „... pozwala na efektywne koordynowanie oddzielnych, a czasem niekompatybilnych elementów” .

Uzupełnienie

Ponadto uważam, że należy pamiętać o znaczeniu terminu „klasa” (z tej samej strony, o której wcześniej wspomniano):

Klasa:

Szereg osób lub rzeczy uważanych za tworzące grupę ze względu na wspólne cechy, cechy, cechy lub cechy; uprzejmy;

W ten sposób klasa (lub klasa abstrakcyjna) powinna reprezentować nie tylko wspólne atrybuty (jak interfejs), ale jakąś grupę o wspólnych atrybutach. Interfejs nie musi reprezentować żadnego rodzaju. Musi reprezentować wspólne atrybuty. W ten sposób myślę, że klasy i klasy abstrakcyjne mogą być używane do przedstawiania rzeczy, które nie powinny często zmieniać jego aspektów, jak człowiek będący ssakiem, ponieważ reprezentuje on pewne rodzaje. Rodzaje nie powinny się tak często zmieniać.


1
zbyt dużo puchu, nie sprawiaj, że będzie to dla ludzi bardziej mylące, niż może być.
ganjeii

5

Z perspektywy kodowania

Interfejs może zastąpić klasę abstrakcyjną, jeśli klasa abstrakcyjna ma tylko metody abstrakcyjne. W przeciwnym razie zmiana klasy abstrakcyjnej na interfejs oznacza, że ​​stracisz możliwość ponownego użycia kodu, który zapewnia dziedziczenie.

Z perspektywy projektowania

Zachowaj go jako klasę abstrakcyjną, jeśli jest to relacja „Jest”, a potrzebujesz podzestawu lub całej funkcjonalności. Zachowaj go jako interfejs, jeśli jest to relacja „powinna zrobić”.

Zdecyduj, czego potrzebujesz: tylko egzekwowanie polisy lub ponowne użycie kodu ORAZ polisa.


3

Kilka innych różnic:

Klasy abstrakcyjne mogą mieć metody statyczne, właściwości, pola itp. I operatory, interfejsy nie. Operator rzutowania pozwala na rzutowanie do / z klasy abstrakcyjnej, ale nie pozwala na rzutowanie do / z interfejsu.

Tak więc możesz samodzielnie używać klasy abstrakcyjnej, nawet jeśli nigdy nie jest ona zaimplementowana (poprzez elementy statyczne) i nie możesz w żaden sposób korzystać z interfejsu samodzielnie.


w Javie interfejs może mieć zmienną składową, ale domyślnie stają się publicznie statyczne .. więc interfejs może mieć pola statyczne
Jitendra Vispute

Tak interfejs może mieć pola statyczne. ALE interfejs nie może mieć metod statycznych.
uczeń
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.