Dlaczego interfejsy są przydatne?


158

Od jakiegoś czasu studiuję i koduję w C #. Ale nadal nie mogę zrozumieć przydatności interfejsów. Za mało przynoszą na stół. Poza dostarczeniem sygnatur funkcji nic nie robią. Jeśli pamiętam nazwy i podpis funkcji, które należy zaimplementować, nie ma takiej potrzeby. Są one po to, aby upewnić się, że wspomniane funkcje (w interfejsie) są zaimplementowane w klasie dziedziczącej.

C # to świetny język, ale czasami daje wrażenie, że najpierw Microsoft tworzy problem (nie zezwalając na wielokrotne dziedziczenie), a następnie zapewnia rozwiązanie, które jest dość żmudne.

Takie jest moje zrozumienie oparte na ograniczonym doświadczeniu w kodowaniu. Jakie jest twoje zdanie na temat interfejsów? Jak często z nich korzystasz i co sprawia, że ​​robisz to?


55
„Jeśli pamiętam nazwy i podpis funkcji, które należy zaimplementować, nie ma takiej potrzeby”. To stwierdzenie sprawia, że ​​podejrzewam, że powinieneś przyjrzeć się nieco zaletom języków statycznych .
Steven Jeuris,

37
Zapomnij o C #, zapomnij o Javie, zapomnij o języku. To po prostu myślenie w kategoriach OO. Zachęcam was do zebrania materiałów do czytania od takich ludzi jak Robert C. Martin, Martin Fowler, Michael Feathers, Gang of Four itd., Ponieważ pomoże to rozwinąć wasze myślenie.
Anthony Pegram,

27
Ponad dwa lata zajęło mi zrozumienie, do czego służą interfejsy. Moja sugestia: przestudiuj Wzory projektowe. Ponieważ większość z nich polega na interfejsach, szybko zrozumiesz, dlaczego są one tak przydatne.
Oliver Weiler,

37
Musisz się wiele nauczyć, przyjacielu.
ChaosPandion,

60
@ChaosPandion wszyscy musimy się wiele nauczyć
kenwarner

Odpowiedzi:


151

Są one po to, aby upewnić się, że wspomniane funkcje (w interfejsie) są zaimplementowane w klasie dziedziczącej.

Poprawny. Jest to wystarczająco niesamowita korzyść, aby uzasadnić tę funkcję. Jak powiedzieli inni, interfejs jest umownym obowiązkiem wdrożenia pewnych metod, właściwości i zdarzeń. Istotną zaletą języka o typie statycznym jest to, że kompilator może zweryfikować, czy umowa, na której opiera się Twój kod, jest rzeczywiście spełniona.

To powiedziawszy, interfejsy są dość słabym sposobem na przedstawienie zobowiązań umownych. Jeśli chcesz silniejszego i bardziej elastycznego sposobu reprezentowania zobowiązań umownych, zapoznaj się z funkcją Kod umów, która została dostarczona z ostatnią wersją Visual Studio.

C # to świetny język, ale czasami daje wrażenie, że najpierw Microsoft tworzy problem (nie zezwalając na wielokrotne dziedziczenie), a następnie zapewnia rozwiązanie, które jest dość żmudne.

Cieszę się, że ci się podoba.

Wszystkie złożone projekty oprogramowania są wynikiem porównywania między sobą sprzecznych funkcji i próby znalezienia „optymalnego punktu”, który daje duże korzyści przy niewielkich kosztach. Dzięki bolesnemu doświadczeniu nauczyliśmy się, że języki, które pozwalają na wielokrotne dziedziczenie w celu współużytkowania implementacji, mają stosunkowo niewielkie zalety i stosunkowo duże koszty. Zezwolenie na wielokrotne dziedziczenie tylko na interfejsach, które nie współużytkują szczegółów implementacji, daje wiele korzyści z wielokrotnego dziedziczenia bez większości kosztów.


Właśnie czytałem „Microsoft tworzy problem, nie dopuszczając wielokrotnego dziedziczenia” i pomyślałem, że Eric Lippert będzie miał coś do powiedzenia na ten temat.
konfigurator

Bardziej istotne dla tej odpowiedzi: Eric, odsyłasz pytającego do Code Contracts, ale są one żałośnie niepełne; cokolwiek innego niż najbardziej podstawowe umowy nie jest wykonalne przez moduł sprawdzania statycznego. Próbowałem użyć Code Contract w małym projekcie; Dodałem setki wierszy dla każdej metody określającej wszystko, co mogłem na temat danych wejściowych i wyjściowych, a jednak musiałem dodać tak wiele Assumewywołań, dla przypadków takich jak tablica lub elementy wyliczeniowe. Po dodaniu wszystkiego i zobaczeniu statycznie zweryfikowanego bałaganu, który miałem, przywróciłem kontrolę źródła, ponieważ obniżyło to jakość mojego projektu.
konfigurator

17
@configurator: Są niekompletne, ponieważ nie mogą być kompletne; statyczna weryfikacja programu z dowolnymi umowami jest równoznaczna z rozwiązaniem problemu zatrzymania. (Na przykład możesz napisać umowę kodową, która mówi, że argumenty metody muszą być kontrprzykładem do ostatniego twierdzenia Fermata; ale statyczny weryfikator nie będzie w stanie zweryfikować, że nie ma takich argumentów.) musisz go rozsądnie wykorzystać, jeśli oczekujesz, że weryfikator statyczny zakończy swoją pracę przed śmiercią wszechświata. (Jeśli uważasz, że BCL nie ma wystarczających adnotacji: zgadzam się.)
Eric Lippert

2
Oczekuję, że zda sobie sprawę, że gdy jedna metoda obiecuje, że tablica wyników lub wyliczenie nie zawierają wartości NULL, metoda używająca może użyć wartości w miejscach, które nie zezwalają na wartości NULL. To jedyna rzecz, jakiej się spodziewałem, że tak się nie stanie, i było to zbyt ważne, aby można było z niej korzystać; wszystko inne to tylko bonus. To powiedziawszy, wiem, że weryfikacja statyczna nie może być kompletna, dlatego uważam, że nie jest dobrą alternatywą poleganie na kontraktach.
konfigurator

9
+1 za „Cóż, cieszę się, że ci się podoba”. I wszystkie inne rzeczy.
Robert S.

235

wprowadź opis zdjęcia tutaj

W tym przykładzie PowerSocket nie wie nic więcej o innych obiektach. Wszystkie obiekty zależą od zasilania dostarczanego przez PowerSocket, więc implementują IPowerPlug i dzięki temu mogą się z nim połączyć.

Interfejsy są przydatne, ponieważ zapewniają kontrakty, z których obiekty mogą współpracować bez konieczności wzajemnego poznawania się.


4
Lewy dolny obiekt nie wygląda tak, jakby implementował IPowerPlug =)
Steven Striga

100
Cholera, ale musimy użyć wzorca adaptera, aby móc korzystać z IPowerPlug w różnych krajach!
Steven Jeuris,

Olinowanie urządzenia do pracy wokół interfejsu nie jest bezpieczne (ale niektórzy nadal to robią, powołując się na wydajność jako niesprawdzoną wymówkę) i może prowadzić do pożarów.
YoungJohn,

4
Ta odpowiedź jest po prostu niesamowita ...
Mario Garcia

Samo wskazanie tej odpowiedzi nie mówi - w tym konkretnym przykładzie - dlaczego użycie interfejsu byłoby lepsze niż dziedziczenie. Ktoś nowy w interfejsach może zapytać, dlaczego nie wszyscy dziedziczą po np. MainsPoweredDevice, które zapewnia wszystkie funkcje wtyczek, a gniazdo akceptuje wszystko, co pochodzi od MainsPoweredDevice.
ingredient_15939

145

Poza dostarczeniem sygnatur funkcji nic nie robią. Jeśli pamiętam nazwy i podpis funkcji, które należy zaimplementować, nie ma takiej potrzeby

Celem interfejsów nie jest przypomnienie sobie, jaką metodę wdrożyć, tutaj jest zdefiniowanie umowy . W przykładzie Foreach P.Brian.Mackey (który okazuje się błędny , ale nas to nie obchodzi), IEnumerable definiuje umowę między foreach a dowolną wymienną rzeczą. Mówi: „Kimkolwiek jesteś, dopóki trzymasz się umowy (implementuj IEnumerable), obiecuję ci, że będę iterował wszystkie twoje elementy”. I to świetnie (dla języka niedynamicznego).

Dzięki interfejsom można uzyskać bardzo niskie sprzężenie między dwiema klasami.



Nie lubię używać terminu „pisanie kaczek”, ponieważ oznacza to różne rzeczy dla różnych ludzi. Używamy dopasowania wzorca dla pętli „foreach”, ponieważ kiedy została zaprojektowana, IEnumerable <T> był niedostępny. Używamy dopasowywania wzorców dla LINQ, ponieważ system typu C # jest zbyt słaby, aby przechwycić potrzebny nam „wzorzec monady”; potrzebujesz czegoś takiego jak system typu Haskell.
Eric Lippert,

@Eric: Czy „dopasowanie wzorca” nie ma tego samego problemu? Kiedy to słyszę, myślę, że F # / Scala / Haskell. Ale myślę, że to szerszy pomysł niż pisanie kaczek.
Daniel

@Daniel: Tak, chyba tak. To chyba sześć z pół tuzina innych!
Eric Lippert,

36

Interfejsy są najlepszym sposobem na utrzymanie dobrze oddzielonych konstrukcji.

Podczas pisania testów przekonasz się, że konkretne klasy nie będą działać w twoim środowisku testowym.

Przykład: Chcesz przetestować klasę zależną od klasy usługi dostępu do danych . Jeśli ta klasa rozmawia z usługą internetową lub bazą danych - test jednostkowy nie uruchomi się w twoim środowisku testowym (a ponadto zamienił się w test integracyjny).

Rozwiązanie? Użyj interfejsu dla usługi dostępu do danych i udaj ten interfejs, aby przetestować swoją klasę jako jednostkę.

Z drugiej strony, WPF i Silverlight wcale nie grają z interfejsami, jeśli chodzi o wiązanie. To dość paskudna zmarszczka.


2
słyszeć słyszeć! Interfejsy opracowano w celu rozwiązania innych problemów, takich jak polimorfizm. Jednak dla mnie sprawdzają się one podczas wdrażania wzoru wstrzykiwania zależności.
andy

29

Interfejsy są kręgosłupem (statycznego) polimorfizmu! Interfejs jest najważniejszy. Dziedziczenie nie działałoby bez interfejsów, ponieważ podklasy w zasadzie dziedziczą już zaimplementowany interfejs rodzica.

Jak często z nich korzystasz i dlaczego to robisz?

Dość często. Wszystko, co musi być podłączane, to interfejs w moich aplikacjach. Często masz inne, niezwiązane ze sobą klasy, które muszą zapewnić takie samo zachowanie. Nie można rozwiązać takich problemów z dziedziczeniem.

Potrzebujesz różnych algorytmów do wykonywania operacji na tych samych danych? Użyj interfejsu ( patrz wzór strategii )!

Czy chcesz używać różnych implementacji listy? Kod przeciwko interfejsowi, a osoba dzwoniąca nie musi się martwić implementacją!

Uznano za dobrą praktykę (nie tylko w OOP) w zakresie kodowania interfejsów od wieków, z jednego tylko powodu: łatwo jest zmienić implementację, gdy zdasz sobie sprawę, że nie pasuje ona do twoich potrzeb. Jest to dość kłopotliwe, jeśli próbujesz to osiągnąć tylko przy wielokrotnym dziedziczeniu lub sprowadza się do tworzenia pustych klas w celu zapewnienia niezbędnego interfejsu.


1
Nigdy nie słyszałeś o polimorfii, masz na myśli polimorfizm?
Steven Jeuris,

1
Biorąc to pod uwagę, gdyby Microsoft w pierwszej kolejności zezwalał na wielokrotne dziedziczenie, nie byłoby powodu istnienia interfejsów
Pankaj Upadhyay

10
@Pankaj Upadhyay: Wielokrotne dziedziczenie i interfejsy to dwie różne pary butów. Co jeśli potrzebujesz interfejsu dwóch niepowiązanych klas o różnych zachowaniach? Nie można tego rozwiązać przez wielokrotne dziedziczenie. W każdym razie musisz go wdrożyć osobno. A potem będziesz potrzebował czegoś do opisania interfejsu, aby zapewnić zachowanie polimorficzne. Dziedziczenie wielokrotne to ślepa uliczka, którą można schodzić w wielu przypadkach, a prędzej czy później zbyt łatwo jest strzelić sobie w stopę.
Falcon

1
Uprośćmy. Jeśli mój interfejs implementuje dwie funkcje Wyświetlania i Komentowania, a ja mam klasę, która je implementuje. Dlaczego więc nie usunąć interfejsu i korzystać bezpośrednio z funkcji? Mówię tylko, że podają jedynie nazwy funkcji, które należy zaimplementować. Jeśli pamiętamy te funkcje, to po co tworzyć interfejs
Pankaj Upadhyay

9
@Pankaj, jeśli to wszystko, czego potrzebujesz do interfejsu, nie używaj go. Używasz interfejsu, gdy masz program, który chce być nieświadomy każdego aspektu klasy i uzyskiwać do niego dostęp poprzez swój podstawowy typ, tj. Interfejs. Nie muszą znać żadnej z podklas, tylko że jest to typ interfejsu. Jako taki, możesz następnie wywołać zaimplementowaną metodę podklasy poprzez odwołanie do interfejsu obiektu. To jest podstawowa dziedziczenie i rzeczy projektowe. Bez niego równie dobrze możesz użyć C. Nawet jeśli nie użyjesz go wprost, framework nie działałby bez niego.
Jonathan Henson

12

Prawdopodobnie użyłeś foreachi okazało się, że jest to bardzo przydatne narzędzie do iteracji. Czy wiesz, że do działania wymagany jest interfejs, IEnumerable ?

To z pewnością konkretny przypadek przemawiający za użytecznością interfejsu.


5
W rzeczywistości foreach nie wymaga IEnumerable: msdn.microsoft.com/en-us/library/9yb8xew9%28VS.80%29.aspx
Matt H

1
Studiowałem to wszystko, ale to tak, jakby trzymać ucho drugą ręką. Gdyby dozwolone było wielokrotne dziedziczenie, interfejs byłby dalekim wyborem.
Pankaj Upadhyay

2
Interfejsy SĄ wielokrotnym dziedzictwem, o czym często się zapomina. Nie pozwalają jednak na wielokrotne dziedziczenie zachowania i stanu. Mixiny lub cechy umożliwiają wielokrotne dziedziczenie zachowania, ale nie stan wspólny, który powoduje problemy: en.wikipedia.org/wiki/Mixin
Matt H

@Pankaj, offtopic, ale czy masz coś przeciwko, jeśli zapytam, jaki jest twój język ojczysty? „Trzymanie ucha drugą ręką” to świetny idiom i byłem ciekawy, skąd pochodzi.
Kevin

3
@Lodziarz. LOL .... Jestem z Indii. A tutaj jest powszechny idiom, który odzwierciedla robienie łatwych rzeczy w trudny sposób.
Pankaj Upadhyay

11

Interfejsy służą do kodowania obiektów, podobnie jak wtyczka do domowych przewodów. Czy przylutowałbyś radio bezpośrednio do okablowania domu? Co powiesz na odkurzacz? Oczywiście nie. Wtyczka i gniazdo, w które się wpasowuje, tworzą „interfejs” między okablowaniem domu a urządzeniem, które wymaga zasilania z niego. Okablowanie domu nie musi wiedzieć nic o urządzeniu poza tym, że wykorzystuje ono trzy bolcową uziemioną wtyczkę i wymaga zasilania elektrycznego przy 120 VAC <= 15 A. I odwrotnie, urządzenie nie wymaga tajemnej wiedzy na temat okablowania domu, poza tym, że ma jedno lub więcej gniazd z trzema bolcami dogodnie usytuowanych, zapewniających napięcie 120 VAC <= 15 A.

Interfejsy pełnią bardzo podobną funkcję w kodzie. Obiekt może zadeklarować, że określona zmienna, parametr lub typ zwracany jest typu interfejsu. Interfejsu nie można utworzyć bezpośrednio za pomocą newsłowa kluczowego, ale mój obiekt może otrzymać lub znaleźć implementację tego interfejsu, z którą będzie musiał współpracować. Kiedy obiekt ma swoją zależność, nie musi dokładnie wiedzieć, czym jest ta zależność, musi tylko wiedzieć, że może wywoływać metody X, Y i Z zależności. Implementacje interfejsu nie muszą wiedzieć, w jaki sposób będą używane, muszą tylko wiedzieć, że od metod X, Y i Z można oczekiwać określonych podpisów.

Tak więc, wyodrębniając wiele obiektów za tym samym interfejsem, zapewniasz wspólny zestaw funkcji każdemu konsumentowi obiektów tego interfejsu. Nie musisz wiedzieć, że obiekt to, na przykład, Lista, Słownik, LinkedList, Or uporządkowana lista lub cokolwiek innego. Ponieważ wiesz, że wszystkie są elementami IEnumerable, możesz korzystać z metod IEnumerable, aby przejść przez każdy element w tych kolekcjach pojedynczo. Nie musisz wiedzieć, że klasa wyjściowa to ConsoleWriter, FileWriter, NetworkStreamWriter, a nawet MulticastWriter, który przyjmuje inne typy programów piszących; wszystko, co musisz wiedzieć, to to, że wszyscy oni są IWriterami (lub czymkolwiek), a zatem mają metodę „Write”, do której możesz przekazać ciąg znaków, a ten ciąg zostanie wyprowadzony.


7

Chociaż programista (przynajmniej na początku) wyraźnie ma przyjemność mieć wielokrotne dziedziczenie, jest to prawie trywialne pominięcie i nie należy (w większości przypadków) polegać na wielokrotnym dziedziczeniu. Przyczyny tego są złożone, ale jeśli naprawdę chcesz się o tym dowiedzieć, skorzystaj z dwóch najpopularniejszych (według indeksu TIOBE ) języków programowania, które go obsługują: C ++ i Python (odpowiednio 3 i 8).

W Pythonie obsługiwane jest wielokrotne dziedziczenie, jednak programiści są prawie powszechnie niezrozumiani i stwierdzenie, że wiesz, jak to działa, oznacza przeczytanie i zrozumienie tego artykułu na temat: Kolejność rozwiązywania metod . Coś innego, co zdarza się w Pythonie, to interfejsy w pewnym sensie wprowadzone do języka - Zope.Interfaces.

W przypadku C ++, google „diamentowa hierarchia C ++” i zobacz brzydotę, która ma cię objąć. Profesjonaliści z C ++ wiedzą, jak korzystać z wielokrotnego dziedziczenia. Wszyscy inni zwykle bawią się, nie wiedząc, jakie będą wyniki. Inną rzeczą, która pokazuje, jak przydatne są interfejsy, jest fakt, że w wielu przypadkach klasa może wymagać całkowitego zastąpienia zachowania swojego rodzica. W takich przypadkach implementacja nadrzędna jest niepotrzebna i tylko obciąża klasę potomną pamięcią dla prywatnych zmiennych rodzica, co może nie mieć znaczenia w wieku C #, ale ma znaczenie, gdy programujesz osadzone. Jeśli korzystasz z interfejsu, problem nie istnieje.

Podsumowując, interfejsy są, moim zdaniem, istotną częścią OOP, ponieważ egzekwują umowę. Wielokrotne dziedziczenie jest przydatne w ograniczonych przypadkach i zwykle tylko dla facetów, którzy wiedzą, jak go używać. Tak więc, jeśli jesteś początkującym, jesteś traktowany przez brak wielokrotnego dziedziczenia - daje to lepszą szansę, aby nie popełnić błędu .

Historycznie pomysł interfejsu jest zakorzeniony znacznie wcześniej niż specyfikacje projektowe Microsoft C #. Większość ludzi uważa C # za uaktualnienie w stosunku do Javy (w większości sensów) i zgaduje, skąd C # ma swoje interfejsy - Java. Protokół jest starszym słowem dla tej samej koncepcji i jest znacznie starszy niż .NET.

Aktualizacja: Teraz widzę, że mogłem odpowiedzieć na inne pytanie - dlaczego interfejsy zamiast wielokrotnego dziedziczenia, ale wydawało się, że to odpowiedź, której szukałeś. Poza tym język OO powinien mieć przynajmniej jeden z dwóch, a pozostałe odpowiedzi obejmowały twoje pierwotne pytanie.


6

Trudno mi wyobrazić sobie czysty, obiektowy kod C # bez użycia interfejsów. Używasz ich, ilekroć chcesz wymusić dostępność określonej funkcjonalności bez zmuszania klas do dziedziczenia po określonej klasie bazowej, a to pozwala twojemu kodowi mieć odpowiedni poziom (niskiego) sprzężenia.

Nie zgadzam się, że wielokrotne dziedziczenie jest lepsze niż posiadanie interfejsów, nawet zanim zaczniemy argumentować, że wielokrotne dziedziczenie wiąże się z własnym zestawem bólów. Interfejsy to podstawowe narzędzie umożliwiające polimorfizm i ponowne wykorzystanie kodu. Czego więcej potrzeba?


5

Osobiście uwielbiam klasę abstrakcyjną i używam jej bardziej niż interfejsu. Główną różnicą jest integracja z interfejsami .NET, takimi jak IDisposable, IEnumerable i tak dalej ... oraz z interfejsem COM. Ponadto interfejs wymaga nieco mniejszego wysiłku w pisaniu niż klasa abstrakcyjna, a klasa może implementować więcej niż jeden interfejs, podczas gdy może dziedziczyć tylko z jednej klasy.

To powiedziawszy, uważam, że większość rzeczy, do których używałbym interfejsu, są lepiej obsługiwane przez klasę abstrakcyjną. Funkcje czysto wirtualne - funkcje abstrakcyjne - pozwalają zmusić implementator do zdefiniowania funkcji podobnej do tego, w jaki sposób interfejs zmusza implementatora do zdefiniowania wszystkich jego elementów.

Jednak zwykle używasz interfejsu, gdy nie chcesz narzucać określonego projektu superklasie, podczas gdy używałbyś klasy abstrakcyjnej, aby mieć projekt wielokrotnego użytku, który jest już w większości wdrożony.

Używałem interfejsów do pisania środowisk wtyczek przy użyciu przestrzeni nazw System.ComponentModel. Przydają się całkiem.


1
Bardzo dobrze powiedziane! Domyślam się, że spodoba ci się mój artykuł na temat klas abstrakcyjnych. Abstrakcja jest wszystkim .
Steven Jeuris,

5

Mogę powiedzieć, że się do tego odnoszą. Kiedy po raz pierwszy zacząłem uczyć się o OO i C #, ja również nie otrzymałem interfejsów. W porządku. Musimy tylko znaleźć coś, co sprawi, że docenisz wygodę interfejsów.

Pozwól mi spróbować dwóch podejść. I wybacz mi uogólnienia.

Spróbuj 1

Powiedz, że jesteś rodzimym językiem angielskim. Wybierasz się do innego kraju, w którym angielski nie jest językiem ojczystym. Potrzebujesz pomocy. Potrzebujesz kogoś, kto może ci pomóc.

Czy pytasz: „Hej, czy urodziłeś się w Stanach Zjednoczonych?” To jest dziedzictwo.

Czy pytasz: „Hej, mówisz po angielsku”? To jest interfejs.

Jeśli zależy ci na tym, co robi, możesz polegać na interfejsach. Jeśli zależy ci na tym, co jest, polegasz na dziedzictwie.

Można polegać na dziedziczeniu. Jeśli potrzebujesz kogoś, kto mówi po angielsku, lubi herbatę i lubi piłkę nożną, lepiej jest poprosić o Brytyjczyka. :)

Spróbuj 2

Ok, spróbujmy innego przykładu.

Korzystasz z różnych baz danych i musisz zaimplementować klasy abstrakcyjne, aby z nimi pracować. Prześlesz swoją klasę do jakiejś klasy od dostawcy DB.

public abstract class SuperDatabaseHelper
{
   void Connect (string User, string Password)
}

public abstract class HiperDatabaseHelper
{
   void Connect (string Password, string User)
}

Wielokrotne dziedziczenie, mówisz? Wypróbuj to w powyższym przypadku. Nie możesz Kompilator nie będzie wiedział, którą metodę Connect próbujesz wywołać.

interface ISuperDatabaseHelper
{
  void Connect (string User, string Password)
}

interface IHiperDatabaseHelper
{
   void Connect (string Password, string User)
}

Teraz jest coś, z czym możemy współpracować - przynajmniej w języku C # - gdzie możemy jawnie implementować interfejsy.

public class MyDatabaseHelper : ISuperDatabaseHelper, IHiperDatabaseHelper
{
   IHiperDataBaseHelper.Connect(string Password, string User)
   {
      //
   }

   ISuperDataBaseHelper.Connect(string User, string Password)
   {
      //
   }

}

Wniosek

Przykłady nie są najlepsze, ale myślę, że ma to sens.

Interfejsy „dostaniesz” tylko wtedy, gdy poczujesz ich potrzebę. Dopóki nie pomyślisz, że nie są dla ciebie.


Pierwsza próba dała mi głos.
osundblad

1
Od teraz całkowicie wykorzystuję analogię amerykańsko-angielską. To jest fantastyczne.
Bryan Boettcher

Wyjaśnione w prostszy sposób! Fantastyczny.
Aimal Khan

4

Istnieją 2 główne powody:

  1. Brak wielokrotnego dziedziczenia. Możesz dziedziczyć po jednej klasie bazowej i implementować dowolną liczbę interfejsów. To jedyny sposób na „zrobienie” wielokrotnego dziedziczenia w .NET.
  2. Interoperacyjność COM. Wszystko, co będzie musiało być wykorzystane przez „starsze” technologie, będzie musiało mieć zdefiniowane interfejsy.

Punkt 1 jest zdecydowanie powodem i powodem opracowanym przez samych programistów Microsoft
Pankaj Upadhyay

1
@Pankja Właściwie wzięli pomysł interfejsu z Javy (jak duża część funkcji C #).
Oliver Weiler,

3

Korzystanie z interfejsów pomaga systemowi pozostać oddzielonym od systemu, a tym samym łatwiej jest go refaktoryzować, zmieniać i ponownie wdrażać. Jest to bardzo podstawowa koncepcja obiektowej ortodoksji i po raz pierwszy się o niej dowiedziałem, gdy guru C ++ stworzyli „czyste klasy abstrakcyjne”, które są zupełnie równoważne interfejsom.


Oddzielenie jest ważne, ponieważ utrzymuje niezależność różnych komponentów systemu. Że nawet duże zmiany w jednym elemencie nie przenoszą się na inne komponenty. Pomyśl o wtyczkach zasilających jako o interfejsie dla twojej firmy energetycznej (określającej napięcie oraz fizyczne styki i format wtyczki). Dzięki temu interfejsowi narzędzie może całkowicie zmienić sposób wytwarzania energii (np. Korzystać z technologii solarnej), ale żadne z urządzeń nawet nie zauważy zmiany.
miraculixx

3

Same interfejsy nie są bardzo przydatne. Ale kiedy są implementowane przez konkretne klasy, widać, że daje to elastyczność, aby mieć jedną lub więcej implementacji. Zaletą jest to, że obiekt korzystający z interfejsu nie musi wiedzieć, jak idą szczegóły rzeczywistej implementacji - to się nazywa enkapsulacja.


2

Są one najczęściej używane do ponownego użycia kodu. Jeśli kodujesz do interfejsu, możesz użyć innej klasy, która dziedziczy z tego interfejsu i nie wszystko zepsuć.

Są również bardzo przydatne w usługach internetowych, w których chcesz poinformować klienta o tym, co robi klasa (aby mogli go wykorzystać), ale nie chcesz podawać rzeczywistego kodu.


2

Jako młody programista / programista, dopiero ucząc się języka C #, możesz nie widzieć przydatności interfejsu, ponieważ możesz pisać kody za pomocą swoich klas, a kod działa dobrze, ale w prawdziwym scenariuszu budowanie skalowalnej, niezawodnej i łatwej w utrzymaniu aplikacji wymaga użycia niektóre architektoniczne i wzorce, które można uczynić możliwym tylko za pomocą interfejsu, na przykład wstrzyknięcie zależności.


1

Realizacja w świecie rzeczywistym:

Możesz rzutować obiekt jako typ interfejsu:

IHelper h = (IHelper)o;
h.HelperMethod();

Możesz utworzyć listę interfejsu

List<IHelper> HelperList = new List<IHelper>();

Za pomocą tych obiektów można uzyskać dostęp do dowolnej metody lub właściwości interfejsu. W ten sposób możesz zdefiniować interfejs dla swojej części programu. I zbuduj wokół niego logikę. Wtedy ktoś inny może wdrożyć Twój interfejs w swoich obiektach biznesowych. Jeśli BO zmieni się, mogą zmienić logikę komponentów interfejsu i nie wymagają zmiany logiki dla twojego elementu.


0

Interfejsy zapewniają modułowość w stylu wtyczek, zapewniając mechanizm umożliwiający klasom zrozumienie (i zasubskrybowanie) określonych rodzajów komunikatów dostarczanych przez system. Opracuję.

W aplikacji decydujesz, że za każdym razem, gdy formularz jest ładowany lub ponownie ładowany, chcesz wyczyścić wszystkie rzeczy, które hostuje. Definiujesz IClearinterfejs, który implementuje Clear. Ponadto decydujesz, że za każdym razem, gdy użytkownik naciśnie przycisk Zapisz, formularz powinien próbować zachować swój stan. Tak więc wszystko, co przestrzega, ISaveotrzymuje komunikat o zachowaniu swojego stanu. Oczywiście, praktycznie mówiąc, większość interfejsów obsługuje kilka komunikatów.

To, co wyróżnia interfejsy, to to, że wspólne zachowanie można osiągnąć bez dziedziczenia. Klasa, która implementuje dany interfejs, po prostu rozumie, jak się zachować po wydaniu polecenia (komunikat polecenia) lub jak odpowiedzieć na zapytanie (komunikat zapytania). Zasadniczo klasy w Twojej aplikacji rozumieją komunikaty dostarczane przez aplikację. Ułatwia to budowę modułowego systemu, do którego można podłączyć rzeczy.

W większości języków istnieją mechanizmy (takie jak LINQ ) do wysyłania zapytań o rzeczy, których dotyczy interfejs. Pomoże to zwykle wyeliminować logikę warunkową, ponieważ nie będziesz musiał mówić odmiennych rzeczy (które nie są konieczne, wywodzących się z tego samego łańcucha dziedziczenia), jak się zachować podobnie (zgodnie z konkretnym komunikatem). Zamiast tego zbierasz wszystko, co rozumie określoną wiadomość (przestrzega interfejsu) i publikujesz wiadomość.

Na przykład możesz zastąpić ...

Me.PublishDate.Clear()
Me.Subject.Clear()
Me.Body.Clear()

...z:

For Each ctl As IClear In Me.Controls.OfType(Of IClear)()
    ctl.Clear()
Next

Co faktycznie brzmi bardzo podobnie:

Słuchajcie, słuchajcie! Czy wszyscy, którzy rozumieją rozliczenia, proszę Clearteraz!

W ten sposób możemy programowo uniknąć mówienia wszystkim, aby się wyczyściło. A kiedy w przyszłości dodawane są usuwalne elementy, po prostu odpowiadają bez dodatkowego kodu.


0

Oto pseudokod:

class MyClass{

    private MyInterface = new MyInterfaceImplementationB();

    // Code using Thingy 

}

interface MyInterface{

    myMethod();

}

class MyInterfaceImplementationA{ myMethod(){ // method implementation A } }

class MyInterfaceImplementationB{ myMethod(){ // method implementation B } }

class MyInterfaceImplementationC{ myMethod(){ // method implementation C } }

Ostatnie klasy mogą być zupełnie innymi implementacjami.

Jeśli nie jest możliwe wielokrotne dziedziczenie, dziedziczenie narzuca implementację klasy nadrzędnej, czyniąc rzeczy bardziej sztywnymi. Z drugiej strony programowanie w interfejsach może pozwolić na wyjątkową elastyczność kodu lub frameworka. Jeśli kiedykolwiek natkniesz się na przypadek, w którym chciałbyś zamienić klasy w łańcuchu spadkowym, zrozumiesz dlaczego.

Na przykład framework, który zapewnia Reader pierwotnie przeznaczony do odczytu danych z dysku, może zostać ponownie zaimplementowany, aby zrobić coś o tym samym charakterze, ale w zupełnie inny sposób. Na przykład interpretuj kod Morse'a.

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.