Jaka jest różnica między interfejsem a klasą i dlaczego powinienem używać interfejsu, kiedy mogę zaimplementować metody bezpośrednio w klasie?

118

Zdaję sobie sprawę, że to bardzo podstawowe pytanie, ale ankieter zadał mi bardzo podstępny sposób i byłam bezradna :(

Znam tylko materiałową lub teoretyczną definicję interfejsu i zaimplementowałem ją w wielu projektach, nad którymi pracowałem. Ale naprawdę nie rozumiem, dlaczego i jak jest to przydatne.

Nie rozumiem też jednej rzeczy w interfejsie. czyli na przykład używamy

conn.Dispose();w końcu bloku. Ale nie widzę, że klasa implementuje lub dziedziczy klasę IDisposableinterface ( SqlConnection), mam na myśli. Zastanawiam się, jak mogę po prostu nazwać nazwę metody. Również w tym samym miejscu nie rozumiem, jak działa metoda Dispose, ponieważ musimy zaimplementować treść funkcji z naszą własną implementacją dla wszystkich metod interfejsu. W jaki sposób interfejsy są akceptowane lub nazywane jako kontrakty? Te pytania toczyły się w mojej głowie aż do teraz i szczerze mówiąc, nigdy nie widziałem żadnego dobrego wątku, który wyjaśniałby moje pytania w sposób, który mógłbym zrozumieć.

MSDN jak zwykle wygląda bardzo przerażająco i żadna pojedyncza linijka nie jest tam jasna ( ludzie, uprzejmie przepraszam, którzy interesują się rozwojem na wysokim poziomie, jestem przekonany, że każdy kod lub artykuł powinien dotrzeć do umysłu każdego, kto go zobaczy, stąd jak wielu innych mówi, MSDN nie nadaje się ).

Ankieter powiedział:

Ma 5 metod i chętnie zaimplementuje je bezpośrednio w klasie, ale jeśli musisz wybrać klasę abstrakcyjną lub interfejs, którą wybierzesz i dlaczego? Odpowiedziałem mu na wszystkie rzeczy, które przeczytałem na różnych blogach, mówiąc o zaletach i wadach zarówno klasy abstrakcyjnej, jak i interfejsu, ale on nie jest przekonany, próbuje ogólnie zrozumieć „Dlaczego interfejs”. „Dlaczego klasa abstrakcyjna” w ogóle, nawet jeśli mogę zaimplementować te same metody tylko raz i nie zamierzam tego zmieniać.

Nigdzie w sieci nie widzę, mógłbym dostać artykuł, który wyjaśniłby mi jasno o interfejsach i ich działaniu. Jestem jednym z tych wielu programistów, którzy wciąż nie wiedzą o interfejsach (znam teorię i metody, z których korzystałem), ale nie są zadowoleni, że dobrze to zrozumiałem.

Uczeń
źródło
4
Interfejsy też starałem się zrozumieć. Dobre pytanie.
Brian
4
programowanie do abstrakcyjnej umowy, a nie konkretnej implementacji… Krótko mówiąc, oznacza to, że można zastąpić dowolny obiekt, który implementuje interfejs, gdy interfejs jest wymagany.
Mitch Wheat
7
SqlConnectiondziedziczy System.ComponentModel.Componentktóry implementuje IDisposable.
Lee
2
@MitchWheat - To nie ma być przykładem, pytanie dotyczy sposobu SqlConnectionimplementacji IDisposable.
Lee
Och Lee, dzięki temu zrozumiałem, dziękuję. Ale nadal nie wiem, jak i gdzie zdefiniowano funkcjonalność metody „Dispose”.
Learner

Odpowiedzi:

92

Interfejsy są doskonałe, gdy chcesz stworzyć coś takiego:

using System;

namespace MyInterfaceExample
{
    public interface IMyLogInterface
    {
        //I want to have a specific method that I'll use in MyLogClass
        void WriteLog();       
    }

    public class MyClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyClass was Logged");
        }
    }

    public class MyOtherClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyOtherClass was Logged");
            Console.Write("And I Logged it different, than MyClass");
        }
    }

    public class MyLogClass
    {
        //I created a WriteLog method where I can pass as a parameter any object that implements IMyLogInterface.
        public static void WriteLog(IMyLogInterface myLogObject)
        {
            myLogObject.WriteLog(); //So I can use WriteLog here.
        }
    }

    public class MyMainClass
    {
        public void DoSomething()
        {
            MyClass aClass = new MyClass();
            MyOtherClass otherClass = new MyOtherClass();

            MyLogClass.WriteLog(aClass);//MyClass can log, and have his own implementation
            MyLogClass.WriteLog(otherClass); //As MyOtherClass also have his own implementation on how to log.
        }
    }
}

W moim przykładzie mógłbym być programistą, który pisze MyLogClass, a inni programiści mogliby tworzyć swoje klasy, a kiedy chcieli się logować, implementowali interfejs IMyLogInterface. To tak, jakby pytali mnie, co muszą zaimplementować, aby użyć WriteLog()metody w MyLogClass. Odpowiedź, którą znajdą w interfejsie.

Guilherme de Jesus Santos
źródło
3
Hej, to wygląda na bardzo dobry składnik do zrozumienia, naprawdę to doceniam, wielkie dzięki :) :)
Learner
14
Moje pytanie brzmi, czy tworzysz instancję MyClassi MyOtherClassdlaczego nie miałbyś po prostu zadzwonić po co aClass.WriteLog()dodać ten dodatkowy krok. Implementacja WriteLog()pozostałaby inna dla każdej z klas, ale masz już obiekt, więc po co przekazywać go do klasy obsługi?
Zach M.,
Hm, może być tak, że jeśli umieścisz swój przykład logowania na bryłce, innym łatwiej będzie używać twojego loggera, nie znając szczegółów ... ale z drugiej strony, nadal nie jest to jakaś uniwersalna klasa (ea mógłbym napisać interfejs z logowaniem ORAZ poziomami allertów) interfejsy są nadal tylko w twoim zakresie. więc oprócz ciebie, kto na tym korzysta?
user3800527
2
@ZachM. Jeśli mam rację, odpowiedź oznacza, że ​​nie utworzy instancji klas, ale inni programiści utworzą instancję klas i przekażą ją jako parametr do MyLogClass WriteLogmetody. Więc jego metoda może obsłużyć każdy obiekt, który implementuje IMyLogInterface. Oto kolejny interesujący post.
shaijut
1
Moje pytanie brzmi: dlaczego interfejs ??? Powyższy scenariusz można również osiągnąć za pomocą klasy abstrakcyjnej ze wszystkimi metodami abstrakcyjnymi.
Abhijit Ojha
52

Jednym z powodów, dla których używam interfejsów, jest to, że zwiększa to elastyczność kodu. Powiedzmy, że mamy metodę, która jako parametr przyjmuje obiekt klasy Account, na przykład:

public void DoSomething(Account account) {
  // Do awesome stuff here.
}

Problem polega na tym, że parametr metody jest ustalony w kierunku wykonania konta. Jest to w porządku, jeśli nigdy nie potrzebujesz innego rodzaju konta. Weźmy ten przykład, który zamiast tego używa interfejsu konta jako parametru.

public void DoSomething(IAccount account) {
  // Do awesome stuff here.
}

To rozwiązanie nie jest ukierunkowane na implementację, co oznacza, że ​​mogę przekazać mu SuperSavingsAccount lub ExclusiveAccount (oba implementujące interfejs IAccount) i uzyskać inne zachowanie dla każdego zaimplementowanego konta.

user2211290
źródło
45

Interfejsy to umowy, których muszą przestrzegać realizatorzy. Klasy abstrakcyjne pozwalają na kontrakty i współdzielone implementacje - coś, czego interfejsy nie mogą mieć. Klasy mogą implementować i dziedziczyć wiele interfejsów. Klasy mogą rozszerzać tylko jedną klasę abstrakcyjną.

Dlaczego interfejs

  • Nie masz domyślnej ani udostępnionej implementacji kodu
  • Chcesz udostępniać umowy dotyczące danych (usługi internetowe, SOA)
  • Masz różne implementacje dla każdego implementatora interfejsu ( IDbCommandma SqlCommandi OracleCommandktóre implementują interfejs w określony sposób )
  • Chcesz obsługiwać wielokrotne dziedziczenie .

Dlaczego abstrakcyjne

SliverNinja - MSFT
źródło
2
@Silver Czytałem większość tego, co wpisujesz na blogach, ale staram się zrozumieć praktycznie. Zrobiłem usługi WCF, ujawnione interfejsy (ale była to tylko pojedyncza samodzielna aplikacja bez upstream lub downstream). Dlatego nie mogłem tego właściwie zrozumieć, chociaż bardzo dobrze zaprojektowałem i zaimplementowałem interfejsy. Moje pytanie brzmi: praktycznie po prostu udostępniasz nazwy metod, co oznacza kontrakt? JAK TO JEST PRZYDATNE :( Wiem, że to po prostu wymusza zaimplementowanie wszystkich metod, ale poza tym w jaki sposób? W twoim poście powyżej o interfejsie, drugi punkt mówi: udostępnij, oznacza to, czy możesz podać praktyczny przykład tego w czasie rzeczywistym
Uczeń
1
Aby uzyskać praktyczny przykład dotyczący interfejsów i architektury SOA, udostępniamy nasze interfejsy WCF ( DataContracts) w zestawie .NET ( np. Contracts.Shared.dll ), aby klienci klienta .NET mogli łatwo współdziałać przy użyciuChannelFactory ( unikając generowania kodu za pomocą dodawania odwołania do usługi itp. ) lub używając Add Service Reference with Shared Types
SliverNinja - MSFT
Jeśli zadeklaruję tylko metody abstrakcyjne wewnątrz klasy abstrakcyjnej, to klasa abstrakcyjna będzie działać jako interfejs, to po co nam interfejs?
Abhijit Ojha
25

wprowadź opis obrazu tutaj

W tym przykładzie PowerSocket nie wie nic więcej o innych obiektach. Wszystkie obiekty zależą od mocy dostarczanej przez PowerSocket, więc implementują IPowerPlug i robiąc to, mogą się z nim łączyć.

Interfejsy są przydatne, ponieważ zapewniają kontrakty, których obiekty mogą używać do współpracy bez konieczności poznawania innych elementów.

Brijesh Rana
źródło
Ma to sens, ale wciąż staram się zrozumieć, czy nie mógłbyś po prostu utworzyć klasy bazowej dla PowerSocket i wszystkich innych rzeczy dziedziczyć ją w razie potrzeby. Technicznie gniazda zasilania nie mają pojęcia o innych klasach.
altaaf.hussein
Myślę, że ponieważ wielokrotne dziedziczenie nie jest dozwolone w języku C #
Hugh Seagraves
22

Jednym słowem - ze względu na polimorfizm !

Jeśli „Programujesz w interfejsie, a nie implementację”, możesz wstrzyknąć do metody różne obiekty, które mają ten sam interfejs (typ), jako argument. W ten sposób kod metody nie jest powiązany z żadną implementacją innej klasy, co oznacza, że ​​jest zawsze otwarty do pracy z nowo utworzonymi obiektami tego samego interfejsu. (Zasada otwarcia / zamknięcia)

  • Przyjrzyj się wstrzykiwaniu zależności i zdecydowanie przeczytaj Wzorce projektowe - elementy oprogramowania obiektowego wielokrotnego użytku firmy GOF.
Gabriel C. Troia
źródło
4

C # nie ma pisania typu kaczego - tylko dlatego, że wiesz, że określona metoda jest zaimplementowana w zestawie konkretnych klas, nie oznacza, że ​​możesz traktować je tak samo, jeśli chodzi o wywołanie tej metody. Implementacja interfejsu umożliwia traktowanie wszystkich klas implementujących go jako rzeczy tego samego typu, w odniesieniu do tego, co ten interfejs definiuje.

Erix
źródło
3
Możesz uzyskać coś w rodzaju ducktyping w .net4 z typem dynamicznym.
Tony Hopkinson
4

Uważam, że zadając te pytania przelano już wiele krwi i wielu próbuje rozwiązać ten problem, wyjaśniając terminy podobne do robotów, których żaden normalny człowiek nie może zrozumieć.

Więc najpierw. aby dowiedzieć się, dlaczego interfejsy i dlaczego są abstrakcyjne, musisz dowiedzieć się, do czego służą. Osobiście nauczyłem się tych dwóch, stosując klasę fabryczną. znajdziesz dobre tuturial pod tym linkiem

Przejdźmy teraz do linku, który już podałem.

Masz klasę pojazdu, która może się zmienić w zależności od wymagań użytkownika (np. Dodanie ciężarówki , czołgu , samolotu itp.)

public class clsBike:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Bike");
    }
    #endregion
}

i

public class clsCar:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Car");
    }
    #endregion
}

i obie mają opcję Contract Ihoice, która po prostu mówi, że Moja klasa powinna mieć metodę Kup

public interface IChoice
{
    string Buy();
}

Teraz widzisz, ten interfejs wymusza tylko metodę, Buy()ale pozwala dziedziczonej klasie zdecydować, co zrobić, gdy ją zaimplementują. To jest ograniczenie interfejsu, używając samego interfejsu, możesz skończyć powtarzając pewne zadanie, które możemy zaimplementować automatycznie za pomocą abstact. Na naszym przykładzie powiedzmy, że zakup każdego pojazdu ma rabat.

public abstract class Choice
{
    public abstract string Discount { get; }
    public abstract string Type { get; }
    public string Buy()
    {
       return "You buy" + Type + " with " + Discount;
}
public class clsBike: Choice
{
    public abstract string Discount { get { return "10% Discount Off"; } }
    public abstract string Type { get { return "Bike"; } }
}

public class clsCar:Choice
{
    public abstract string Discount { get { return " $15K Less"; } }
    public abstract string Type { get { return "Car"; } }
}

Teraz używając klasy Factory, możesz osiągnąć to samo, ale używając abstrakcji, pozwalasz klasie bazowej wykonać Buy()metodę.

Podsumowując: Kontrakty interfejsu pozwalają klasie dziedziczącej wykonać implementację, podczas gdy Kontrakty klasy abstrakcyjnej mogą zainicjować implementację (co może przesłonić klasę Inherit)

dr Crow
źródło
2

Za pomocą interfejsu możesz wykonać następujące czynności:

1) Twórz oddzielne interfejsy, które oferują różne części implementacji, co pozwala na bardziej spójny interfejs.

2) Zezwalaj na wiele metod o tej samej nazwie między interfejsami, ponieważ hej, nie masz sprzecznej implementacji, tylko podpis.

3) Możesz wersjonować i wydzielać swój interfejs niezależnie od implementacji, zapewniając dotrzymanie umowy.

4) Twój kod może opierać się raczej na abstrakcji niż konkrecji, co pozwala na inteligentne wstrzykiwanie zależności, w tym wstrzykiwanie testowych Mocków itp.

Jestem pewien, że powodów jest o wiele więcej, to tylko kilka.

Klasa abstrakcyjna pozwala mieć częściowo konkretną podstawę do pracy, nie jest to to samo, co interfejs, ale ma swoje własne cechy, takie jak możliwość tworzenia częściowej implementacji przy użyciu wzorca metody szablonu.

Jeff Watkins
źródło
Zignorowałeś najważniejszą rzecz: implementacja interfejsu sprawia, że ​​twoja klasa może być używana przez dowolny kod wymagający implementacji tego interfejsu, bez konieczności posiadania przez kod, o którym mowa, nic o twojej klasie.
supercat
2

Jako prawdziwa próbka odpowiedzi @ user2211290:

Oba Arrayi Listmają interfejs IList. Poniżej mamy a string[]i a List<string>i sprawdzamy oba z nich jedną metodą używając IList :

string[] col1 = { "zero", "one", "two", "three", "four"};
List<string> col2 = new List<string>{ "zero", "one", "two", "three"};

//a methode that manipulates both of our collections with IList
static void CheckForDigit(IList collection, string digit)
{
    Console.Write(collection.Contains(digit));
    Console.Write("----");
    Console.WriteLine(collection.ToString()); //writes the type of collection
}

static void Main()
{
    CheckForDigit(col1, "one");   //True----System.String[]
    CheckForDigit(col2, "one");   //True----System.Collections.Generic.List`1[System.String]



//Another test:

    CheckForDigit(col1, "four");   //True----System.String[]
    CheckForDigit(col2, "four");   //false----System.Collections.Generic.List`1[System.String]
}
Farzin Kanzi
źródło
1

Możesz dziedziczyć tylko z jednej klasy abstrakcyjnej. Możesz dziedziczyć z wielu interfejsów. To określa, czego używam w większości przypadków.

Zaletą klasy abstrakcyjnej byłoby to, że można mieć podstawową implementację. Jednak w przypadku IDisposable domyślna implementacja jest bezużyteczna, ponieważ klasa bazowa nie wie, jak poprawnie wyczyścić rzeczy. Zatem interfejs byłby bardziej odpowiedni.

Jeow Li Huan
źródło
1

Zarówno klasa abstrakcyjna, jak i interfejs są kontraktami.

Ideą kontraktu jest określenie zachowania. Jeśli mówisz, że wdrożyłeś, zgodziłeś się na kontrakt.

Wybór abstraktu zamiast interfejsu jest następujący.

Każdy nie abstrakcyjny element potomny klasy abstrakcyjnej zaimplementuje kontrakt.

przeciw

Każda klasa, która implementuje interfejs, będzie implementować kontrakt.

Tak więc używasz abstrakcji, gdy chcesz określić pewne zachowanie, które wszyscy potomkowie muszą zaimplementować i zapisać siebie, definiując oddzielny interfejs, ale teraz wszystko, co spełnia ten skutecznie zagregowany kontrakt, musi być potomkiem.

Tony Hopkinson
źródło
1

Interfejsy mają stanowić abstrakcję (archetyp) abstrakcji (klas) rzeczywistości (obiektów).

Interfejsy mają określać warunki umowy bez zapewnienia implementacji zapewnianej przez klasy.

Interfejsy to specyfikacje:

  • Interfejsy są artefaktami czasu projektowania, które określają nieruchome zachowanie koncepcji, ponieważ jest ona sama i statyczna.

  • Klasy to artefakty czasu implementacji, które określają mobilną strukturę rzeczywistości, gdy oddziałuje i porusza się.

Co to jest interfejs?

Kiedy obserwujesz kota, możesz powiedzieć, że jest to zwierzę, które ma cztery łapy, głowę, pień, ogon i sierść. Widać, że potrafi chodzić, biegać, jeść i miauczeć. I tak dalej.

Właśnie zdefiniowałeś interfejs z jego właściwościami i operacjami. W związku z tym nie zdefiniowałeś żadnego modus operandi, a jedynie cechy i możliwości, nie wiedząc, jak rzeczy działają: masz zdefiniowane zdolności i różnice.

W związku z tym nie jest jeszcze klasą, mimo że w UML nazywamy ją klasą na diagramie klas, ponieważ możemy zdefiniować szeregowych i chronionych członków, aby zacząć mieć głęboki wgląd w artefakt. Nie daj się tutaj zmylić, ponieważ w UML interfejs jest nieco inną rzeczą niż interfejs w C #: jest jak częściowy punkt dostępu do atomu abstrakcji. W związku z tym powiedzieliśmy, że klasa może implementować wiele interfejsów. Jako taki jest to to samo, ale nie, ponieważ interfejsy w C # są używane zarówno do abstrakcji, jak i do ograniczania tej abstrakcji jako punktu dostępu. Ma dwa różne zastosowania. Zatem klasa w UML reprezentuje pełny interfejs sprzęgający z klasą programistyczną, podczas gdy interfejs UML reprezentuje interfejs oddzielający część klasy programistycznej. W rzeczy samej, diagram klas w UML nie zajmuje się implementacją, a wszystkie jego artefakty znajdują się na poziomie interfejsu programistycznego. Podczas gdy mapujemy klasy UML do klas programowania, jest to transpozycja abstrakcji abstrakcyjnej na abstrakcję konkretną. Istnieje subtelność, która wyjaśnia dychotomię między dziedziną projektowania a dziedziną programowania. Tak więc klasa w UML jest klasą programistyczną z punktu widzenia interfejsu programistycznego, biorąc pod uwagę wewnętrzne ukryte rzeczy.

Interfejsy pozwalają także na symulację wielokrotnego dziedziczenia, gdy nie są dostępne w niezręczny sposób. Na przykład klasa cat zaimplementuje interfejs cat wywodzący się z interfejsu zwierzęcia. Ta klasa kotów będzie również implementowała następujące interfejsy: chodzić, biegać, jeść i wydawać dźwięki. To rekompensuje brak wielokrotnego dziedziczenia na poziomie klasy, ale za każdym razem musisz wszystko ponownie zaimplementować i nie możesz w najlepszym przypadku uwzględniać rzeczywistości tak, jak robi to sama rzeczywistość.

Aby to zrozumieć, możemy odwołać się do kodowania obiektowego Pascal, w którym definiuje się w jednostce interfejs i sekcje implementacyjne. W interfejsie definiujesz typy, aw implementacji implementujesz typ:

unit UnitName;

interface

type
  TheClass = class
  public
    procedure TheMethod;
  end;

implementation

class procedure TheClass.TheMethod;
begin
end;

Tutaj sekcja interfejsu pasuje do projektu klasy UML, podczas gdy typy interfejsów są innymi rzeczami.

Tak więc w naszej branży mamy jedno słowo, interfejs , na określenie dwóch różnych, ale podobnych rzeczy i jest to źródło nieporozumień.

Na przykład w języku C # interfejsy programistyczne pozwalają zrekompensować brak prawdziwego polimorfizmu generycznego na typach otwartych bez rzeczywistego osiągnięcia celu, ponieważ utraciłeś silnie wpisaną elastyczność.

W końcu interfejsy są niezbędne, aby umożliwić niekompatybilnym systemom komunikację bez martwienia się o implementację i zarządzanie obiektami w pamięci, jak wprowadzono w (rozproszonym) modelu obiektów wspólnych.

Co to jest klasa?

Po zdefiniowaniu redukcji rzeczywistości z zewnętrznego punktu widzenia, możesz opisać ją z wewnętrznej perspektywy: jest to klasa, w której definiujesz przetwarzanie danych i zarządzanie wiadomościami, aby umożliwić ożywienie rzeczywistości, którą hermetyzujesz, i interakcję dzięki do obiektów przy użyciu instancji.

Tak więc w UML zdajesz sobie sprawę z fraktalnego zanurzenia się w kołach maszyny i opisujesz stany, interakcje i tak dalej, aby móc wdrożyć abstrakcję fragmentu rzeczywistości, z którym chcesz się poradzić.

Jako taka klasa abstrakcyjna jest w pewnym sensie odpowiednikiem interfejsu z punktu widzenia kompilatora.

Więcej informacji

Protokół (programowanie obiektowe)

C # - interfejsy

C # - Klasy

Olivier Rogier
źródło
1

Pozwólcie, że opowiem o latających tosterach.

latający toster

Oczywiście istnieje wiele sytuacji, w których można zbudować działający system oprogramowania bez deklarowania lub implementowania żadnych interfejsów: każdy projekt oprogramowania zorientowanego obiektowo można zrealizować przy użyciu wyłącznie klas.

Z drugiej strony, każdy system oprogramowania może być również zaimplementowany w języku asemblera lub jeszcze lepiej w kodzie maszynowym. Powodem, dla którego używamy mechanizmów abstrakcji, jest to, że mają tendencję do ułatwiania rzeczy. Takim mechanizmem abstrakcji są interfejsy.

Tak więc tak się składa, że ​​istnieją pewne nietrywialne projekty zorientowane obiektowo, które są o wiele łatwiejsze do zaimplementowania, jeśli używasz interfejsów, że interfejsy praktycznie stają się niezbędne w takich przypadkach.

Te nietrywialne projekty mają do czynienia z dziedziczeniem wielokrotnym, które w swojej „prawdziwej” postaci ma miejsce, gdy klasa dziedziczy nie tylko z jednej klasy bazowej, ale z dwóch lub więcej klas bazowych. Ta prawdziwa forma nie jest możliwa w C #, ale zanim powstały języki takie jak C # i Java, językiem, który rządził był C ++, który w pełni obsługuje prawdziwe wielokrotne dziedziczenie. Niestety, prawdziwe wielokrotne dziedziczenie okazało się niezbyt dobrym pomysłem, ponieważ ogromnie komplikuje konstrukcję języka, a także rodzi różne problemy, na przykład słynny „Diamentowy problem”. (Zobacz odpowiedź „Na czym dokładnie polega problem z wielokrotnym dziedziczeniem?”, Odpowiedź J. Francisa )

Jeśli więc ktoś chciałby zbudować klasę „latającego tostera”, odziedziczyłby po jakiejś istniejącej klasie „tostera”, a także po jakiejś istniejącej klasie „latającej”. Rodzaj problemu, na jaki mogli się natknąć, polegał na tym, że zasilacz klasy tostera był prawdopodobnie gniazdem ściennym, podczas gdy źródłem zasilania klasy maszyn latających był prawdopodobnie pokarm dla gołębi, a powstała nowa klasa byłaby albo w jakiś sposób mieć oba, albo nie byłoby jasne, który z nich miałby. (Problem z diamentami).

Twórcy języków takich jak C # i Java zdecydowali się nie zezwalać na prawdziwe wielokrotne dziedziczenie, aby zachować prostotę języka i uniknąć pułapek takich jak Diamentowy Problem. Jednak pewna forma wielokrotnego dziedziczenia jest nadal konieczna (lub przynajmniej bardzo pożądana), więc w tych językach wprowadzono interfejsy jako sposób obsługi mniejszej formy wielokrotnego dziedziczenia, jednocześnie unikając problemów i złożoności prawdziwego wielokrotnego dziedziczenia.

W tej pomniejszej formie wielokrotnego dziedziczenia nie możesz mieć klasy, która dziedziczy z więcej niż jednej klasy bazowej, ale możesz przynajmniej dziedziczyć z jednego lub więcej interfejsów. Tak więc, jeśli chcesz zbudować latający toster, nie możesz dziedziczyć zarówno z jakiejś istniejącej klasy tostera, jak i z jakiejś istniejącej klasy latającej, ale co możesz zrobić, to odziedziczyć z istniejącej klasy tostera, a następnie odsłonić latający interfejs, który sam implementujesz, prawdopodobnie używając jakichkolwiek środków, które już odziedziczyłeś po tosterze.

Tak więc, chyba że kiedykolwiek poczujesz potrzebę stworzenia klasy, która agreguje dwa różne i niepowiązane ze sobą zestawy funkcji, nie będziesz potrzebować żadnej formy wielokrotnego dziedziczenia, więc nie będziesz musiał deklarować ani implementować żadnych interfejsów.

Mike Nakis
źródło
0

Interfejsy pozwalają projektantowi klas bardzo jasno przedstawić dostępne metody dla użytkownika końcowego. Są też integralną częścią polimorfizmu.

poy
źródło
Dobrze powiedziane na pierwszym oświadczeniu. Ale nie rozumiem twojego drugiego stwierdzenia, czy mógłbyś rozwinąć go na przykładzie czasu rzeczywistego?
Learner
0

Nie będę publikował definicji interfejsu w odniesieniu do klasy abstrakcyjnej, ponieważ myślę, że znasz bardzo dobrze teorię i zakładam, że znasz zasady SOLID, więc przejdźmy do praktyczności.

Jak wiesz, interfejsy nie mogą mieć żadnego kodu, więc różnica jest dość łatwa do zrozumienia.

jeśli potrzebujesz zainicjować właściwość swojej klasy udostępniającej konstruktor lub chcesz udostępnić część implementacji, klasa abstrakcyjna byłaby dobrym rozwiązaniem dla interfejsu, który nie pozwoliłby ci tego zrobić.

Więc ogólnie rzecz biorąc, powinieneś preferować klasę abstrakcyjną od interfejsów, gdy musisz dostarczyć konstruktor lub dowolny kod do klienta, który odziedziczy / rozszerzy twoją klasę

Massimiliano Peluso
źródło
-2

Klasy abstrakcyjne są tworzone dla jednostek powiązanych, w których jako interfejsy mogą być używane dla jednostek niepowiązanych.

Na przykład, jeśli mam dwie istoty, które mówią Zwierzę i Człowiek, to wybiorę interfejs, w którym tak, jakbym musiał szczegółowo powiedzieć Tygrys, lew i chcę się powiązać ze Zwierzęciem, wybiorę klasę Animal Abstract.

będzie wyglądać jak poniżej

   Interface             
   ____|____
  |        |
Animal   Human



  Animal (Abstract class)
   __|___
  |      |
Tiger   Lion
Manoj
źródło