Interface vs Base class

Kiedy powinienem używać interfejsu, a kiedy klasy bazowej?

Czy zawsze powinien to być interfejs, jeśli nie chcę definiować podstawowej implementacji metod?

Jeśli mam zajęcia z psem i Kotem. Dlaczego miałbym chcieć zaimplementować IPet zamiast PetBase? Rozumiem posiadanie interfejsów dla ISheds lub IBarks (IMakesNoise?), ponieważ można je umieścić na podstawie pet by pet, ale nie rozumiem, którego użyć do ogólnego zwierzaka.

Author: Howler, 2008-09-11

30 answers

Weźmy przykład z klasy psa i kota i zilustrujmy używając C#:

Zarówno Pies, jak i kot są zwierzętami, w szczególności czworonożnymi ssakami(zwierzęta są zbyt ogólne). Załóżmy, że masz klasę abstrakcyjną, dla obu z nich:
public abstract class Mammal

Ta klasa bazowa prawdopodobnie będzie miała domyślne metody, takie jak:

  • Feed
  • Mate

Wszystkie z nich są zachowaniami, które mają mniej więcej taką samą implementację pomiędzy albo gatunek. Aby to zdefiniować należy:

public class Dog : Mammal
public class Cat : Mammal

Przypuśćmy, że są inne ssaki, które zwykle zobaczymy w zoo:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

To nadal będzie ważne, ponieważ w centrum funkcjonalności Feed() i Mate() nadal będą takie same.

Jednak żyrafy, nosorożce i hipopotamy nie są zwierzętami, z których można zrobić zwierzęta domowe. Tutaj przydałby się interfejs:
public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

Realizacja powyższej umowy nie będzie taka sama pomiędzy kot i pies; umieszczenie ich implementacji w abstrakcyjnej klasie do dziedziczenia będzie złym pomysłem.

Twoje definicje psów i kotów powinny teraz wyglądać następująco:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

Teoretycznie możesz nadpisać je z wyższej klasy bazowej, ale zasadniczo interfejs pozwala na dodanie do klasy tylko tych rzeczy, których potrzebujesz, bez potrzeby dziedziczenia.

W konsekwencji, ponieważ zazwyczaj można dziedziczyć tylko z jednej klasy abstrakcyjnej (w większości statycznie wpisywanych języków OO, czyli... wyjątki obejmują C++), ale być w stanie zaimplementować wiele interfejsów, pozwala na konstruowanie obiektów w ściśle zgodnie z wymaganiami podstawy.

 463
Author: Jon Limjap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-04 15:56:27

Cóż, Josh Bloch powiedział sam w efektywna Java 2D :

Preferuj interfejsy zamiast klas abstrakcyjnych

Niektóre główne punkty:

  • Istniejące klasy można łatwo doposażyć w celu wdrożenia nowego interfejs . Wszystko co musisz zrobić to dodać wymagane metody, jeśli jeszcze nie exist and add an implements clause to deklaracja klasy.

  • Interfejsy są idealne do definiowania mieszanin . Luźno mówiąc, a mixin jest typem, który klasa może wdrożyć oprócz jego " podstawowej wpisz " aby zadeklarować, że zapewnia jakieś opcjonalne zachowanie. Na przykład, Porównywalny jest interfejs mixin, który pozwala klasie zadeklarować, że jej instancje są uporządkowane w odniesieniu do inne wzajemnie porównywalne obiekty.

  • Interfejsy umożliwiają budowę typu niehierarchicznego Framework . Hierarchie typów to świetne do organizacji niektórych rzeczy, ale inne rzeczy nie spadają zgrabnie w sztywna hierarchia.

  • Interfejsy umożliwiają bezpieczne, potężne ulepszenia funkcjonalności[[13]} poprzez zawijanie-na idiom klasowy. W przypadku stosowania abstrakcyjnych klas do definiowania typów, ty zostaw programistę, który chce dodać funkcjonalność bez alternatywy, ale do korzystania z dziedziczenia.

Ponadto można łączyć zalety interfejsów i klas abstrakcyjnych przez dostarczenie abstrakcyjnego szkieletu Klasa implementacji do każdego nietrywialny interfejs, który eksportujesz.

Z drugiej strony, interfejsy są bardzo trudne do ewoluowania. Jeśli dodasz metodę do interfejsu, złamie ona wszystkie jej implementacje.

PS. Kup książkę. Jest dużo bardziej szczegółowy.

 134
Author: Marcio Aguiar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-04 11:46:00

Nowoczesnym stylem jest zdefiniowanie IPet i PetBase.

Zaletą interfejsu jest to, że inny kod może go używać bez żadnych powiązań z innym kodem wykonywalnym. Całkowicie " czyste."Również interfejsy mogą być mieszane.

Ale klasy bazowe są przydatne dla prostych implementacji i popularnych narzędzi. Zapewnij więc abstrakcyjną klasę bazową, aby zaoszczędzić czas i Kod.

 106
Author: Jason Cohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 15:34:45

Interfejsy i klasy bazowe reprezentują dwie różne formy relacji.

Dziedziczenie (klasy bazowe) reprezentują relację "is-a". Np. pies lub kot "jest" zwierzakiem. Ta relacja zawsze reprezentuje (pojedynczy) cel klasy (w połączeniu z "zasadą pojedynczej odpowiedzialności" ).

Interfejsy reprezentują natomiast Dodatkowe funkcje klasy. Nazwałbym to związkiem" jest", jak w "Foo is disposable", stąd Interfejs IDisposable W C#.

 93
Author: Thomas Danecker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-15 11:05:27

Interfejsy

  • definiuje umowę pomiędzy dwoma modułami. Nie może mieć żadnej implementacji.
  • większość języków pozwala na implementację wielu interfejsów
  • modyfikowanie interfejsu to przełomowa zmiana. Wszystkie implementacje wymagają rekompilacji/modyfikacji.
  • wszyscy członkowie są jawni. Implementacje muszą zaimplementować wszystkich członków.
  • interfejsy pomagają w Odsprzęganiu. Możesz użyć mock frameworków, aby wyśledzić wszystko, co znajduje się za interfejsem
  • interfejsy zwykle wskazuje rodzaj zachowania
  • implementacje interfejsu są od siebie oddzielone / oddzielone

Klasy bazowe

  • pozwala na dodanie jakiejś domyślnej implementacji, którą otrzymujesz za darmo przez wyprowadzenie
  • z wyjątkiem C++, możesz czerpać tylko z jednej klasy. Nawet jeśli może z wielu klas, to zwykle zły pomysł.
  • Zmiana klasy bazowej jest stosunkowo łatwa. Pochodne nie muszą robić niczego specjalnego
  • baza klasy mogą deklarować funkcje chronione i publiczne, które mogą być dostępne przez pochodne
  • abstrakcyjne klasy bazowe nie mogą być łatwo wyśmiewane jak interfejsy
  • klasy bazowe zwykle wskazują hierarchię typów (IS A)
  • derywacje klas mogą zależeć od pewnego podstawowego zachowania (mają skomplikowaną wiedzę o implementacji rodzica). Rzeczy mogą być niechlujne, jeśli dokonasz zmiany w implementacji bazy dla jednego faceta i złamiesz innych.
 57
Author: Gishu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-08 20:29:52

Ogólnie rzecz biorąc, powinieneś faworyzować interfejsy zamiast klas abstrakcyjnych. Jednym z powodów użycia klasy abstrakcyjnej jest wspólna implementacja pomiędzy konkretnymi klasami. Oczywiście, nadal powinieneś zadeklarować interfejs (IPet) i mieć klasę abstrakcyjną (PetBase) implementującą ten interfejs.Korzystając z małych, odrębnych interfejsów, można używać wielokrotności, aby jeszcze bardziej zwiększyć elastyczność. Interfejsy umożliwiają maksymalną elastyczność i przenośność typów ponad granicami. Przy przekazywaniu referencji przez granice, zawsze przechodzą przez interfejs, a nie konkretny typ. Pozwala to odbiorcy określić konkretną realizację i zapewnia maksymalną elastyczność. Jest to absolutnie prawdziwe podczas programowania w trybie TDD / BDD.

The Gang of Four stwierdził w swojej książce "ponieważ dziedziczenie naraża podklasę na szczegóły implementacji jej rodzica, często mówi się, że"dziedziczenie łamie enkapsulację". Wierzę, że to prawda.

 56
Author: Kilhoffer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 17:53:39

Jest to dość specyficzne dla. NET, ale książka Framework Design Guidelines twierdzi, że klasy ogólne dają większą elastyczność w rozwijającym się frameworku. Po wysłaniu interfejsu nie masz szansy go zmienić bez złamania kodu, który używał tego interfejsu. Z klasą można ją jednak modyfikować, a nie łamać kodu, który się do niej łączy. Tak długo, jak dokonasz odpowiednich modyfikacji, które obejmują dodanie nowych funkcjonalności, będziesz mógł rozszerzyć i ewoluować swoje kod.

Krzysztof Cwalina says on page 81:

Podczas pracy nad trzema wersjami. NET Framework rozmawiałem o tych wytycznych z kilkoma programistami z naszego zespołu. Wielu z nich, w tym ci, którzy początkowo nie zgadzali się z wytycznymi, stwierdziło, że żałują, że wysłali niektóre API jako interfejs. Nie słyszałem nawet o jednym przypadku, w którym ktoś żałował, że wysłał klasę.

To powiedziane, że na pewno jest miejsce w klasyfikacji generalnej. Jako ogólne wytyczne zawsze dostarczaj abstrakcyjną implementację klasy bazowej interfejsu, jeśli nie jako przykład sposobu implementacji interfejsu. W najlepszym przypadku ta klasa bazowa zaoszczędzi dużo pracy.

 46
Author: fryguybob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 16:18:49

Juan,

Lubię myśleć o interfejsach jako o sposobie scharakteryzowania klasy. Konkretna Klasa Psów Rasowych, powiedzmy YorkshireTerrier, może być potomkiem klasy psów rodzicielskich, ale jest również wdraża IFurry, IStubby, IYippieDog. Tak więc Klasa określa czym jest klasa, ale interfejs mówi nam o niej różne rzeczy.

Zaletą tego jest to, że pozwala mi na przykład zebrać wszystkie IYippieDog ' s i wrzucić je do mojej kolekcji Ocean. Więc teraz mogę dotrzeć przez konkretny zestaw obiektów i znaleźć te, które spełniają kryteria patrzę na bez kontroli Klasy zbyt uważnie.

Uważam, że interfejsy naprawdę powinny definiować podzbiór publicznego zachowania klasy. Jeśli definiuje wszystkie publiczne zachowania dla wszystkich klas, które implementują, to zazwyczaj nie musi istnieć. Nie mówią mi nic użytecznego.

Ta myśl jest jednak sprzeczna z ideą, że każda klasa powinna mieć interfejs i należy kodować do interfejs. To jest w porządku, ale kończy się na wielu interfejsach jeden do jednego do klas i to sprawia, że rzeczy mylące. Rozumiem, że chodzi o to, że tak naprawdę nic nie kosztuje, a teraz możesz z łatwością wymieniać rzeczy. Jednak uważam, że rzadko to robię. Większość czasu jestem po prostu modyfikowanie istniejącej klasy w miejscu i mają dokładnie te same problemy, które zawsze zrobiłem, jeśli publiczny interfejs tej klasy wymaga zmiany, z wyjątkiem teraz muszę zmienić go w dwóch miejsca.

Więc jeśli myślisz tak jak ja, zdecydowanie powiesz, że kot i pies są ip. Jest to charakterystyka, która pasuje do nich obu.

Drugą częścią tego jest to, czy powinni mieć tę samą klasę bazową? Pytanie brzmi, czy należy je ogólnie traktować jako to samo. Z pewnością oba są zwierzętami, ale czy to pasuje do tego, jak będziemy ich używać razem.

Powiedz, że chcę zebrać wszystkie klasy zwierząt i umieścić je w moim pojemniku Arki.

Lub do muszą być ssakami? Może potrzebujemy jakiejś fabryki doju zwierząt krzyżowych?

Czy w ogóle trzeba je ze sobą łączyć? Czy wystarczy po prostu wiedzieć, że oba są Ipetable?

Często odczuwam pragnienie wyprowadzenia całej hierarchii klasowej, kiedy naprawdę potrzebuję tylko jednej klasy. Robię to w oczekiwaniu, że kiedyś będę tego potrzebować i zazwyczaj nigdy tego nie robię. Nawet kiedy to robię, zazwyczaj muszę zrobić wiele, aby to naprawić. To dlatego, że pierwszą klasą, którą tworzę, nie jest pies, Nie mam tyle szczęścia, zamiast tego dziobak. Teraz cała moja hierarchia klasowa opiera się na dziwnym przypadku i mam dużo zmarnowanego kodu.

Możesz też w pewnym momencie zauważyć, że nie wszystkie koty są Ipetowalne(jak ten bezwłosy). Teraz możesz przenieść ten interfejs do wszystkich klas pochodnych, które pasują. Przekonasz się, że znacznie mniej przełomowa Zmiana, że nagle koty nie są już pochodną PettableBase.

 19
Author: Flory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 21:32:07

Oto podstawowa i prosta definicja interfejsu i klasy bazowej:

  • klasa bazowa = dziedziczenie obiektów.
  • Interface = dziedziczenie funkcjonalne.

Cheers

 15
Author: RWendi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-20 20:26:31

Zalecam używanie kompozycji zamiast dziedziczenia, o ile to możliwe. Używa interfejsów, ale używa obiektów członkowskich do implementacji bazowej. W ten sposób można zdefiniować fabrykę, która konstruuje obiekty tak, aby zachowywały się w określony sposób. Jeśli chcesz zmienić zachowanie, tworzysz nową metodę factory (lub abstrakcyjną fabrykę), która tworzy różne typy obiektów podrzędnych.

W niektórych przypadkach może się okazać, że Twoje podstawowe obiekty w ogóle nie potrzebują interfejsów, jeśli wszystkie mutowalne zachowanie jest zdefiniowane w obiektach pomocniczych.

Więc zamiast IPet lub PetBase, możesz skończyć z Pet, który ma parametr IFurBehavior. Parametr IFurBehavior jest ustawiany za pomocą metody CreateDog () PetFactory. To właśnie ten parametr jest wywoływany dla metody shed ().

Jeśli to zrobisz, zauważysz, że Twój kod jest znacznie bardziej elastyczny, a większość prostych obiektów radzi sobie z bardzo podstawowymi zachowaniami w całym systemie.

Polecam ten wzór nawet w wielu dziedziczeniach języki.

 12
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 19:03:01

Wyjaśnione dobrze w tym Java World article

Osobiście używam interfejsów do definiowania interfejsów - tj. części projektu systemu, które określają, jak coś powinno być dostępne.

Nie jest rzadkością, że będę miał klasę implementującą 1 lub więcej interfejsów.

Klasy abstrakcyjne używam jako podstawy do czegoś innego.

Poniżej znajduje się wyciąg z wyżej wymienionego artykułu JavaWorld.com artykuł, autor Tony Sintes, 04/20/01


Interface vs. abstract class

Wybór interfejsów i klas abstrakcyjnych nie jest propozycją albo / albo. Jeśli chcesz zmienić swój projekt, zrób z niego interfejs. Możesz jednak mieć abstrakcyjne klasy, które zapewniają pewne domyślne zachowanie. Klasy abstrakcyjne są doskonałymi kandydatami wewnątrz RAM aplikacyjnych.

Klasy abstrakcyjne pozwalają definiować pewne zachowania; zmuszają podklasy do dostarczania innych. Na przykład, jeśli mają framework aplikacji, klasa abstrakcyjna może dostarczać domyślne usługi, takie jak obsługa zdarzeń i wiadomości. Usługi te umożliwiają podłączenie aplikacji do struktury aplikacji. Istnieją jednak pewne funkcje specyficzne dla aplikacji, które może wykonywać tylko Twoja aplikacja. Takie funkcje mogą obejmować zadania uruchamiania i zamykania, które często zależą od aplikacji. Więc zamiast próbować zdefiniować to zachowanie, abstrakcyjna klasa bazowa może zadeklarować abstract shutdown i metody uruchamiania. Klasa bazowa wie, że potrzebuje tych metod, ale klasa abstrakcyjna pozwala twojej klasie przyznać, że nie wie, jak wykonać te akcje; wie tylko, że musi zainicjować te akcje. Gdy nadejdzie czas uruchomienia, Klasa abstract może wywołać metodę uruchamiania. Gdy klasa bazowa wywoła tę metodę, Java wywołuje metodę zdefiniowaną przez klasę potomną.

Wielu programistów zapomina, że klasa, która definiuje abstrakcyjną metodę, może również wywołać tę metodę. Klasy abstrakcyjne są doskonałym sposobem tworzenia zaplanowanych hierarchii dziedziczenia. Są również dobrym wyborem dla klas nieleaf w hierarchii klas.

Klasa kontra interfejs

Niektórzy mówią, że należy definiować wszystkie klasy pod względem interfejsów, ale myślę, że rekomendacja wydaje się nieco ekstremalna. Używam interfejsów, gdy widzę, że coś w moim projekcie będzie się często zmieniać.

Na przykład wzorzec strategii pozwala zamienić nowe algorytmy i procesy na programuj bez zmiany obiektów, które ich używają. Odtwarzacz multimedialny może wiedzieć, jak odtwarzać płyty CD, MP3 i pliki wav. Oczywiście, nie chcesz na twardo kodować tych algorytmów odtwarzania do odtwarzacza; to utrudni dodanie nowego formatu, takiego jak AVI. Co więcej, Twój kod będzie zaśmiecony bezużytecznymi instrukcjami case. Aby dodać obrazę do obrażeń, będziesz musiał aktualizować te oświadczenia za każdym razem, gdy dodasz nowy algorytm. Podsumowując, nie jest to bardzo obiektowy sposób na program.

Dzięki wzorcowi strategii możesz po prostu zamknąć algorytm za obiektem. Jeśli to zrobisz, możesz w każdej chwili udostępnić nowe wtyczki multimedialne. Nazwijmy klasę Plug-in MediaStrategy. Obiekt ten miałby jedną metodę: playStream (Stream s). Aby dodać nowy algorytm, po prostu rozszerzamy naszą klasę algorytmów. Teraz, gdy program napotka nowy typ mediów, po prostu przekazuje odtwarzanie strumienia do naszej strategii medialnej. Oczywiście, będziesz potrzebował trochę hydrauliki. aby poprawnie utworzyć strategie algorytmów, będziesz potrzebować.

To doskonałe miejsce do korzystania z interfejsu. Zastosowaliśmy wzór Strategii, który wyraźnie wskazuje miejsce w projekcie, które się zmieni. Dlatego powinieneś zdefiniować strategię jako interfejs. Zazwyczaj powinieneś faworyzować interfejsy zamiast dziedziczenia, jeśli chcesz, aby obiekt miał określony typ; w tym przypadku MediaStrategy. Opieranie się na dziedziczeniu dla tożsamości typu jest niebezpieczne; blokuje cię w konkretnym hierarchia dziedziczenia. Java nie pozwala na wielokrotne dziedziczenie, więc nie można rozszerzyć czegoś, co daje użyteczną implementację lub większą tożsamość typu.

 12
Author: Richard Harrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-16 20:39:24

Należy również pamiętać, aby nie dać się porwać w OO (Zobacz blog ) i zawsze modelować obiekty w oparciu o wymagane zachowanie, jeśli projektujesz aplikację, w której jedynym wymaganym zachowaniem była ogólna nazwa i gatunek dla zwierzęcia, to potrzebujesz tylko jednego zwierzęcia klasy z właściwością dla nazwy, zamiast milionów klas dla każdego możliwego zwierzęcia na świecie.

 10
Author: Sijin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-22 14:18:10

I have a rough rule-of-thumb

Funkcjonalność: prawdopodobnie będzie różna we wszystkich częściach: interfejs.

Dane i funkcjonalność, części będą w większości takie same, części Inne: klasa abstrakcyjna.

Dane i funkcjonalność, faktycznie działające, jeśli rozszerzone tylko z niewielkimi zmianami: zwykła (konkretna) klasa

Dane i funkcjonalność, bez planowanych zmian: zwykła (konkretna) klasa z końcowym modyfikatorem.

Danych, oraz może funkcjonalność: tylko do odczytu: Członkowie enum.

Jest to bardzo szorstki i gotowy i wcale nie ściśle określony, ale istnieje spektrum od interfejsów, gdzie wszystko ma być zmienione do enums, gdzie wszystko jest naprawione trochę jak plik Tylko do odczytu.

 8
Author: Akhil Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-14 08:56:40

Interfejsy powinny być małe. Naprawdę mały. Jeśli naprawdę rozkładasz swoje obiekty, to prawdopodobnie interfejsy będą zawierały tylko kilka bardzo specyficznych metod i właściwości.

Klasy abstrakcyjne to skróty. Czy są rzeczy, które wszystkie pochodne PetBase dzielić, że można kodować raz i być zrobione z? Jeśli tak, to czas na abstrakcyjną klasę.

Klasy abstrakcyjne również ograniczają. Podczas gdy dają one świetny skrót do wytwarzania obiektów potomnych, każdy dany obiekt może zaimplementować tylko jedną klasę abstrakcyjną. Wiele razy uważam to za ograniczenie klas abstrakcyjnych i dlatego używam wielu interfejsów.

Klasy abstrakcyjne mogą zawierać kilka interfejsów. Twoja klasa petbase abstract może zaimplementować IPet (zwierzęta mają właścicieli) i IDigestion (zwierzęta jedzą, a przynajmniej powinny). Jednak PetBase prawdopodobnie nie wdroży IMammal, ponieważ nie wszystkie zwierzęta są ssakami i nie wszystkie ssaki są zwierzętami domowymi. Możesz dodać MammalPetBase, który rozszerza PetBase i dodać IMammal. FishBase może mieć PetBase i dodać IFish. IFish miałby ISwim i IUnderwaterBreather jako interfejsy.

Tak, mój przykład jest bardzo skomplikowany jak na prosty przykład, ale to część wspaniałej rzeczy w tym, jak interfejsy i abstrakcyjne klasy współpracują ze sobą.

 7
Author: Jarrett Meyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 15:34:03

Przypadek dla klas bazowych nad interfejsami został dobrze wyjaśniony w wytycznych dotyczących kodowania. NET:

Base Classes vs. Interfaces Typ interfejsu jest częściowy opis wartości, potencjalnie obsługiwane przez wiele typów obiektów. Użycie klasy bazowe zamiast interfejsów o ile to możliwe. Z wersji perspektywy, zajęcia są bardziej elastyczne niż interfejsy. Z klasą możesz statek w wersji 1.0, a następnie w wersji 2.0 Dodaj nową metodę do klasy. O ile metoda nie jest abstrakcyjna, wszelkie istniejące klasy pochodne kontynuują funkcjonować bez zmian.

Ponieważ interfejsy nie obsługują implementation inheritance, the wzorzec, który dotyczy klas robi Nie dotyczy interfejsów. Dodawanie a metoda do interfejsu jest równoważna aby dodać metodę abstrakcyjną do bazy Klasa; każda klasa, która implementuje interfejs się zepsuje, ponieważ Klasa nie implementuje nowej metody. Interfejsy są odpowiednie w następujące sytuacje:

  1. kilka niepowiązanych klas chce wspierać protokół.
  2. klasy te mają już ustalone klasy bazowe (dla przykład, niektóre z nich to kontrolki interfejsu użytkownika (UI) , i niektóre są XML Web services).
  3. agregacja nie jest odpowiednia ani praktyczna. We wszystkich innych sytuacje, dziedziczenie klas jest lepszym modelem.
 6
Author: YeahStu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-18 20:25:45

Ważną różnicą jest to, że możesz dziedziczyć tylko Jedną klasę bazową, ale możesz zaimplementować Wiele interfejsów. Więc chcesz używać klasy bazowej tylko wtedy, gdy jesteś absolutnie pewien, że nie będziesz musiał dziedziczyć innej klasy bazowej. Dodatkowo, jeśli zauważysz, że twój interfejs jest coraz większy, powinieneś zacząć szukać podziału go na kilka logicznych elementów, które definiują niezależną funkcjonalność, ponieważ nie ma reguły, której twoja klasa nie może zaimplementować wszystkie (lub że można zdefiniować inny interfejs, który po prostu dziedziczy je wszystkie, aby je pogrupować).

 4
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-04 21:23:35

Kiedy po raz pierwszy zacząłem uczyć się programowania obiektowego, popełniłem prosty i prawdopodobnie powszechny błąd polegający na używaniu dziedziczenia do współdzielenia wspólnego zachowania - nawet tam, gdzie zachowanie to nie było istotne dla natury obiektu.

Aby dalej budować na przykładzie często używanym w tym konkretnym pytaniu, istnieje Wiele rzeczy, które są petable - dziewczyny, samochody, rozmyte koce... - więc mogłem mieć klasę pieszczotliwą, która zapewniała to powszechne zachowanie i różne klasy dziedziczące po nim.

Jednak bycie głaskanym nie jest częścią natury żadnego z tych obiektów. Istnieją znacznie ważniejsze pojęcia, które istotne dla ich natury - dziewczyna jest osobą, samochód jest pojazdem lądowym, kot jest ssakiem...

Zachowania powinny być najpierw przypisane do interfejsów (w tym domyślnego interfejsu klasy) i promowane do klasy bazowej tylko wtedy, gdy są (a) wspólne dla dużej grupy klas, które są podzbiorami Klasa większa - w tym samym sensie ,że " kot " i "osoba" są podzbiorami "ssaka".

Haczyk polega na tym, że po zrozumieniu projektu zorientowanego obiektowo wystarczająco lepiej niż ja na początku, zwykle robisz to automatycznie, nawet o tym nie myśląc. Tak więc Naga prawda stwierdzenia "kod do interfejsu, a nie abstrakcyjnej klasy" staje się tak oczywista, że trudno uwierzyć, że ktoś zawracałby sobie głowę, aby to powiedzieć - i zaczął próbować czytać w nim inne znaczenia.

Inna sprawa Dodam, że jeśli klasa jest czysto abstrakcyjna-Bez nie-abstrakcyjnych, nie-dziedziczonych elementów lub metod narażonych na działanie dzieci, rodziców lub klientów-to dlaczego jest to klasa? Może być zastąpiony, w niektórych przypadkach przez interfejs, a w innych przypadkach przez Null.

 4
Author: Don Edwards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-11 22:14:03

Źródło: http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C # jest wspaniałym językiem, który dojrzał i ewoluował w ciągu ostatnich 14 lat. Jest to świetne rozwiązanie dla nas programistów, ponieważ dojrzały język zapewnia nam mnóstwo funkcji językowych, które są do naszej dyspozycji.

[2]] jednak z dużą mocą staje się dużą odpowiedzialnością. Niektóre z tych funkcji mogą być nadużywane, a czasami trudno jest zrozumieć, dlaczego ty wybrałby użycie jednej funkcji nad inną. Przez lata, cechą, z którą widziałem wielu deweloperów zmagających się jest to, kiedy wybrać użycie interfejsu lub wybrać użycie klasy abstrakcyjnej. Oba mają zalety i wady oraz właściwy czas i miejsce użycia. Ale jak się zdecydować???

Oba zapewniają ponowne wykorzystanie wspólnej funkcjonalności między typami. Najbardziej oczywistą różnicą od razu jest to, że interfejsy nie zapewniają implementacji ich funkcjonalności, podczas gdy klasy abstrakcyjne pozwalają zaimplementować pewne" podstawowe "lub" domyślne "zachowanie, a następnie mają możliwość" nadpisania " tego domyślnego zachowania typami pochodnymi klas, jeśli jest to konieczne.

To wszystko jest dobre i dobre, zapewnia świetne ponowne użycie kodu i przestrzega suchej (nie powtarzaj się) zasady tworzenia oprogramowania. Klasy abstrakcyjne są świetne do wykorzystania, gdy masz związek "jest".

Na przykład: golden retriever "jest" psem w typie. Pudel też. Oboje może szczekać, jak wszystkie psy. Możesz jednak powiedzieć, że park pudli znacznie różni się od" domyślnego " szczekania psów. W związku z tym, to może mieć sens dla Ciebie zaimplementować coś w następujący sposób:

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

Jak widzisz, byłby to świetny sposób na zachowanie suchego kodu i umożliwienie wywołania implementacji klasy bazowej, gdy którykolwiek z typów może polegać na domyślnej Bark zamiast na specjalnej implementacji przypadków. Klasy takie jak GoldenRetriever, Boxer, Lab mogą Wszystkie mogą dziedziczyć" default " (Klasa bass) bez opłat tylko dlatego, że implementują klasę Dog abstract.

Ale jestem pewien, że już o tym wiesz.

Jesteś tutaj, ponieważ chcesz zrozumieć, dlaczego możesz chcieć wybrać interfejs zamiast klasy abstrakcyjnej lub odwrotnie. Jednym z powodów, dla których możesz chcieć wybrać interfejs zamiast klasy abstrakcyjnej, jest brak domyślnej implementacji lub jej brak. Jest to zwykle spowodowane tym, że typy, które realizują interfejs niezwiązany z relacją" jest". W rzeczywistości nie muszą być ze sobą w ogóle powiązane, z wyjątkiem faktu, że każdy typ "jest w stanie" lub ma "ablity", aby coś zrobić lub mieć coś.

Co to ma znaczyć? Cóż, na przykład: człowiek nie jest kaczką...a kaczka nie jest człowiekiem. Dość oczywiste. Jednak zarówno Kaczor, jak i człowiek mają "zdolność" do pływania (biorąc pod uwagę, że człowiek zdał lekcje pływania w 1 klasie :) ). Ponadto, ponieważ kaczka nie jest człowiekiem ani występkiem nie jest to relacja "jest", ale relacja "jest w stanie" i możemy użyć interfejsu, aby zilustrować, że:
// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

Używanie interfejsów takich jak powyższy kod pozwoli Ci przekazać obiekt do metody, która" jest w stanie " coś zrobić. Kod nie obchodzi, jak to robi ... wszystko, co wie, to to, że może wywołać metodę Swim na tym obiekcie i ten obiekt będzie wiedział, które zachowanie w czasie wykonywania w oparciu o jego typ.

Po raz kolejny, dzięki temu Twój kod pozostanie suchy, więc że nie będziesz musiał pisać wielu metod, które wywołują obiekt do preformowania tej samej podstawowej funkcji (ShowHowHumanSwims( human), ShowHowDuckSwims(duck), itd.)

Użycie interfejsu pozwala wywołującym metody nie martwić się o to, jaki typ jest Który lub jak zachowanie jest zaimplementowane. Po prostu wie, że biorąc pod uwagę interfejs, każdy obiekt będzie musiał zaimplementować metodę Swim, więc można ją bezpiecznie wywołać we własnym kodzie i pozwolić na zachowanie metody Swim obsługiwane w ramach własnej klasy.

Podsumowanie:

Więc moją główną zasadą jest używanie abstrakcyjnej klasy, gdy chcesz zaimplementować" domyślną "funkcjonalność dla hierarchii klas lub / i klas lub typów, z którymi pracujesz, współdzielą relację "jest" (np. pudel " to " rodzaj psa).

Z drugiej strony używaj interfejsu, gdy nie masz relacji "jest", ale masz typy, które dzielą "zdolność" do zrobienia czegoś lub posiadania czegoś (np. Kaczor "nie jest" człowiekiem. Jednakże, Kaczor i człowiek dzielą "zdolność" do pływania).

Kolejna różnica między klasami abstrakcyjnymi i interfejsami jest taka, że klasa może zaimplementować jeden do wielu interfejsów, ale klasa może dziedziczyć tylko z jednej klasy abstrakcyjnej(lub dowolnej klasy). Tak, możesz zagnieżdżać klasy i mieć hierarchię dziedziczenia (co wiele programów robi i powinno mieć), ale nie możesz dziedziczyć dwóch klas w jednej pochodnej definicji klasy (zasada ta dotyczy C#. W niektórych innych językach jesteś w stanie zrobić to, zwykle tylko z powodu braku interfejsów w tych językach).

Pamiętaj również, gdy używasz interfejsów do przestrzegania zasady segregacji interfejsów (ISP). ISP twierdzi, że żaden klient nie powinien być zmuszony do polegania na metodach, których nie używa. Z tego powodu interfejsy powinny koncentrować się na konkretnych zadaniach i są zazwyczaj bardzo małe (np. IDisposable, Icomporable).

Kolejna wskazówka: jeśli tworzysz małe, zwięzłe elementy funkcjonalności, użyj interfejsów. Jeśli jesteś projektując duże jednostki funkcjonalne, Użyj klasy abstrakcyjnej.

Mam nadzieję, że to wyjaśni pewne rzeczy dla niektórych ludzi!

Również jeśli możesz wymyślić jakieś lepsze przykłady lub chcesz coś wskazać, zrób to w komentarzach poniżej!

 4
Author: Jason Roell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-09 20:42:00

Poprzednie komentarze na temat używania klas abstrakcyjnych do wspólnej implementacji są zdecydowanie na wagę złota. Jedną z korzyści, o których jeszcze nie wspomniałem, jest to, że użycie interfejsów znacznie ułatwia implementację mock obiektów do celów testów jednostkowych. Zdefiniowanie IPet i PetBase zgodnie z opisem Jasona Cohena pozwala na łatwe kopiowanie różnych warunków danych, bez obciążenia fizycznej bazy danych (dopóki nie zdecydujesz, że nadszedł czas, aby przetestować prawdziwą rzecz).

 3
Author: Scott Lawrence,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 22:01:10

Nie używaj klasy bazowej, chyba że wiesz, co ona oznacza i że ma ona zastosowanie w tym przypadku. Jeśli ma zastosowanie, użyj go, w przeciwnym razie użyj interfejsów. Ale zwróć uwagę na odpowiedź na temat małych interfejsów.

Publiczne Dziedziczenie jest nadużywane w OOD i wyraża o wiele więcej, niż większość deweloperów zdaje sobie sprawę lub jest skłonna żyć. Zobacz zasada substytucyjności Liskowa

W skrócie, jeśli A "jest" B, to a wymaga nie więcej niż B i dostarcza nie mniej niż B, dla każdej metody to exposes.

 3
Author: theschmitzer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-12 21:24:45

Koncepcyjnie interfejs jest używany do formalnego i półformalnego zdefiniowania zestawu metod, które obiekt dostarczy. Formalnie oznacza zbiór nazw metod i podpisów, semi-formalnie oznacza czytelną dla człowieka dokumentację związaną z tymi metodami. Interfejsy są tylko opisami API (w końcu API oznacza Application Programmer Interface), nie mogą zawierać żadnej implementacji i nie można używać ani uruchamiać interfejsu. Wyrażają tylko wyraźną umowę o tym, jak należy wchodzić w interakcje z obiektem.

Klasy dostarczają implementację, mogą zadeklarować, że implementują zero, jeden lub więcej interfejsów. Jeśli klasa ma być dziedziczona, Konwencja ma poprzedzać nazwę klasy "Base".

Istnieje rozróżnienie między klasą bazową a abstrakcyjną klasą bazową (ABC). Interfejs ABCs mix i implementacja razem. Abstrakt poza programowaniem komputerowym oznacza "streszczenie", czyli "Abstract = = Interface". Abstrakt Klasa bazowa może wtedy opisywać Zarówno interfejs, jak i pustą, częściową lub całkowitą implementację, która ma być dziedziczona.

Opinie na temat tego, kiedy używać interfejsów a abstrakcyjne klasy bazowe a tylko klasy będą się bardzo różnić w zależności zarówno od tego, co się rozwija, jak i w jakim języku się rozwija. Interfejsy są często kojarzone tylko ze statycznie typowanymi językami, takimi jak Java lub C#, ale dynamicznie typowane języki mogą również mieć Interfejsy i abstrakcyjne Klasy Podstawowe. Na przykład w Pythonie rozróżnienie jest jasne między klasą, która deklaruje, że implementuje Interfejs, a obiektem, który jest instancją klasy i mówi się, że Dostarcza Ten interfejs. W języku dynamicznym możliwe jest, że dwa obiekty, które są instancjami tej samej klasy, mogą zadeklarować, że zapewniają całkowicie różne interfejsy. W Pythonie jest to możliwe tylko dla atrybutów obiektów, podczas gdy metody są współdzielone pomiędzy wszystkimi obiektami klasy. Jednak w Rubim, obiekty mogą mieć metody dla poszczególnych instancji, więc jest możliwe, że interfejs między dwoma obiektami tej samej klasy może się różnić tak, jak zapragnie programista (jednak Ruby nie ma żadnego jawnego sposobu deklarowania interfejsów).

W językach dynamicznych interfejs do obiektu jest często domyślnie zakładany, albo poprzez introspekcję obiektu i zapytanie go, jakie metody on dostarcza (sprawdź, zanim przeskoczysz) lub najlepiej po prostu próbując użyj żądanego interfejsu na obiekcie i wyłapuj wyjątki, jeśli obiekt nie zapewnia tego interfejsu (łatwiej poprosić o przebaczenie niż pozwolenie). Może to prowadzić do" fałszywych alarmów", w których dwa interfejsy mają tę samą nazwę metody, ale różnią się semantycznie, jednak kompromis polega na tym, że Twój kod jest bardziej elastyczny, ponieważ nie musisz nadmiernie określać z góry, aby przewidzieć wszystkie możliwe zastosowania kodu.

 3
Author: Wheat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-15 22:34:03

Inną opcją, o której należy pamiętać, jest użycie relacji "has-a", aka "jest zaimplementowana w kategoriach" lub "kompozycja."Czasami jest to czystszy, bardziej elastyczny sposób na uporządkowanie rzeczy niż używanie dziedziczenia" is-a".

Może nie mieć sensu logicznie mówić, że pies i kot "mają" zwierzaka, ale unika się typowych pułapek dziedziczenia wielokrotnego:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Tak, ten przykład pokazuje, że w robieniu rzeczy jest dużo duplikacji kodu i brak elegancji tędy. Ale należy również docenić, że pomaga to utrzymać psa i Kota oddzielone od klasy zwierząt domowych (w tym Pies i kot nie mają dostępu do prywatnych członków zwierząt domowych), i pozostawia miejsce dla psa i kota do dziedziczenia z czegoś innego-ewentualnie klasy ssaków.

Skład jest preferowany, gdy nie jest wymagany prywatny dostęp i nie musisz odwoływać się do psa i kota za pomocą ogólnych odniesień/wskaźników dla zwierząt domowych. Interfejsy zapewniają tę ogólną zdolność odniesienia i mogą pomóc w cięciu w dół na szczegółowość kodu, ale mogą również zaciemniać rzeczy, gdy są one słabo zorganizowane. Dziedziczenie jest przydatne, gdy potrzebujesz dostępu prywatnego członka, a korzystając z niego, zobowiązujesz się do wysokiego łączenia klas psów i kotów z klasą zwierząt domowych, co jest stromym kosztem.

Pomiędzy dziedziczeniem, kompozycją i interfejsami nie ma jednego sposobu, który byłby zawsze właściwy i pomaga rozważyć, w jaki sposób wszystkie trzy opcje mogą być używane w harmonii. Z trzech, dziedziczenie jest zazwyczaj opcja, która powinna być używana najmniej często.

 3
Author: Parappa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-03 22:37:18

Preferuj interfejsy zamiast klas abstrakcyjnych

Uzasadnienie, główne punkty do rozważenia [dwa już wymienione tutaj] to:

  • interfejsy są bardziej elastyczne, ponieważ klasa może zaimplementować wiele interfejsy. Ponieważ Java nie posiada wielu dziedziczeń, za pomocą klasy abstrakcyjne uniemożliwiają użytkownikom korzystanie z innych klas hierarchia. Ogólnie rzecz biorąc, preferujemy interfejsy, gdy nie ma domyślnych wdrożenia lub państwa. Kolekcje Javy oferują dobre przykłady z to (mapa, zestaw itp.).
  • klasy abstrakcyjne mają tę zaletę, że pozwalają lepiej kompatybilność. Gdy klienci korzystają z Interfejsu, nie można go zmienić; jeśli używają klasy abstrakcyjnej, nadal można dodać zachowanie bez łamanie istniejącego kodu. jeśli chodzi o kompatybilność, rozważ użycie klasy abstrakcyjne.
  • nawet jeśli masz domyślne implementacje lub stan wewnętrzny, rozważ oferowanie interfejsu i jego abstrakcyjną implementację. To będzie pomagać klientom, ale nadal pozwalać im na większą swobodę, jeśli pożądane [1].
    Oczywiście temat był omawiany obszernie gdzie indziej [2,3].

[1] dodaje więcej kodu, oczywiście, ale jeśli zwięzłość jest twoim głównym problemem, prawdopodobnie powinieneś unikać Javy w pierwszej kolejności!

[2] Joshua Bloch, skuteczna Java, poz. 16-18.

[3] http://www.codeproject.com/KB/ar ...

 3
Author: Jaimin Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-27 11:43:54

To zależy od twoich wymagań. Jeśli IPet jest wystarczająco prosty, wolałbym to zaimplementować. W przeciwnym razie, jeśli PetBase implementuje mnóstwo funkcjonalności, których nie chcesz powielać, a następnie mieć na to.

Minusem implementacji klasy bazowej jest wymóg override (LUB new) istniejących metod. To sprawia, że są to metody wirtualne, co oznacza, że musisz uważać na to, jak używasz instancji obiektu.

Wreszcie, pojedyncze dziedzictwo. NET zabija mnie. Naiwny przykład: Powiedzmy, że tworzysz kontrolę użytkownika, więc dziedziczysz UserControl. Ale teraz jesteś zablokowany z dziedziczenia PetBase. To zmusza cię do reorganizacji, na przykład do utworzenia PetBase członka klasy, zamiast tego.

 2
Author: spoulson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 15:29:09

@Joel: niektóre języki (np. C++) pozwalają na dziedziczenie wielokrotne.

 2
Author: Einar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 15:30:55

Ja też zwykle nie wdrażam, dopóki nie potrzebuję. Preferuję interfejsy zamiast klas abstrakcyjnych, ponieważ daje to nieco większą elastyczność. Jeśli istnieje powszechne zachowanie w niektórych klasach dziedziczących, przenoszę je w górę i tworzę abstrakcyjną klasę bazową. Nie widzę potrzeby obu, ponieważ zasadniczo serwują ten sam cel, a posiadanie obu jest złym zapachem kodu (imho) , że rozwiązanie zostało przerobione.

 2
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 15:31:47

Jeśli chodzi o C#, w niektórych aspektach interfejsy i klasy abstrakcyjne mogą być wymienne. Jednak różnice są następujące: i) interfejsy nie mogą zaimplementować kodu; ii) z tego powodu interfejsy nie mogą wywoływać dalej stosu do podklasy; i iii) tylko klasa abstrakcyjna może być dziedziczona na klasie, podczas gdy wiele interfejsów może być zaimplementowanych na klasie.

 2
Author: CarneyCode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-23 11:51:10

Odkryłem, że wzór interfejsu > Abstrakt > Concrete działa w następującym przypadku użycia:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

Klasa abstrakcyjna definiuje domyślne wspólne atrybuty konkretnych klas, ale wymusza interfejs. Na przykład:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Teraz, ponieważ wszystkie ssaki mają włosy i Sutki (AFAIK, nie jestem zoologiem), możemy przenieść to do abstrakcyjnej klasy bazowej

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

I wtedy konkretne klasy tylko określają, że chodzą wyprostowani.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

Ten projekt jest miło, gdy jest dużo konkretnych klas, a nie chcesz utrzymywać boilerplate tylko po to, aby zaprogramować interfejs. Gdyby nowe metody zostały dodane do interfejsu, złamałoby to wszystkie wynikowe klasy, więc nadal otrzymujesz zalety podejścia interfejsu.

W tym przypadku abstrakt równie dobrze może być konkretny; jednak oznaczenie abstrakcyjne pomaga podkreślić, że ten wzór jest stosowany.

 2
Author: Adam Hughes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-04 18:28:29

Dziedziczenie klasy bazowej powinno mieć relację "is a". Interfejs reprezentuje relację" implementuje". Więc używaj klasy bazowej tylko wtedy, gdy spadkobiercy utrzymają relację is a.

 1
Author: Aaron Fischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 18:23:33

Użyj interfejsów, aby wyegzekwować umowę między rodzinami niepowiązanych klas. Na przykład, możesz mieć wspólne metody dostępu dla klas, które reprezentują kolekcje, ale zawierają radykalnie różne dane, tzn. jedna klasa może reprezentować zestaw wyników z zapytania, podczas gdy druga może reprezentować obrazy w galerii. Ponadto, można zaimplementować wiele interfejsów, co pozwala na mieszanie (i signify) możliwości klasy.

Użyj dziedziczenia, gdy klasy mają wspólny samochód, motocykl, ciężarówka i SUV to wszystkie typy pojazdów drogowych, które mogą zawierać wiele kół, prędkość maksymalna

 1
Author: sunwukung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-14 21:26:25