„Klasa abstrakcyjna” i „interfejs” są podobnymi pojęciami, a interfejs jest bardziej abstrakcyjny z tych dwóch. Jednym z czynników różnicujących jest to, że klasy abstrakcyjne zapewniają w razie potrzeby implementacje metod dla klas pochodnych. Jednak w języku C # ten czynnik różnicujący został zmniejszony dzięki niedawnemu wprowadzeniu metod rozszerzenia, które umożliwiają implementację metod interfejsu. Innym czynnikiem różnicującym jest to, że klasa może odziedziczyć tylko jedną klasę abstrakcyjną (tj. Nie ma wielokrotnego dziedziczenia), ale może implementować wiele interfejsów. To sprawia, że interfejsy są mniej restrykcyjne i bardziej elastyczne. Więc w C #, kiedy powinniśmy używać klas abstrakcyjnych zamiast interfejsów z metodami rozszerzenia?
Godnym uwagi przykładem modelu interfejsu + metody rozszerzenia jest LINQ, w którym funkcjonalność zapytania jest zapewniona dla każdego typu, który implementuje IEnumerable
za pomocą wielu metod rozszerzenia.
źródło
Odpowiedzi:
Kiedy potrzebujesz części klasy do zaimplementowania. Najlepszym przykładem, jaki użyłem, jest wzorzec metody szablonu .
W ten sposób można zdefiniować kroki, które będą podejmowane po wywołaniu funkcji Do (), bez znajomości szczegółów ich implementacji. Klasy pochodne muszą implementować metody abstrakcyjne, ale nie metodę Do ().
Metody rozszerzeń niekoniecznie spełniają część równania „musi być częścią klasy”. Ponadto metody iirc, rozszerzenia nie mogą (jak się wydaje) mieć zasięg publiczny.
edytować
Pytanie jest bardziej interesujące niż pierwotnie przypisałem. Po dalszych badaniach Jon Skeet odpowiedział na takie pytanie w SO na korzyść użycia interfejsów + metod rozszerzenia. Ponadto, potencjał minusem jest przy użyciu odbicia przeciwko hierarchii obiektów zaprojektowanych w ten sposób.
Osobiście mam problem z dostrzeżeniem korzyści płynących ze zmiany obecnie powszechnej praktyki, ale widzę też kilka wad w tym zakresie.
Należy zauważyć, że można zaprogramować w ten sposób w wielu językach za pomocą klas Utility. Rozszerzenia tylko dostarczają cukier składniowy, aby metody wyglądały tak, jakby należały do klasy.
źródło
Do()
zostaną zdefiniowane jako metoda rozszerzenia. Metody pozostawione do definicji klas pochodnych, takie jak,DoThis()
będą członkami głównego interfejsu. Dzięki interfejsom możesz obsługiwać wiele implementacji / dziedziczenia. I zobacz to- odetocode.com/blogs/scott/archive/2009/10/05/…Chodzi o to , co chcesz modelować:
Klasy abstrakcyjne działają w oparciu o dziedziczenie . Będąc tylko specjalnymi klasami podstawowymi, modelują one pewien związek .
Na przykład pies jest zwierzęciem, więc mamy
Ponieważ nie ma sensu tworzenie ogólnego zwierzęcia (jak by to wyglądało?), Tworzymy tę
Animal
klasę podstawowąabstract
- ale nadal jest to klasa podstawowa. ADog
klasa nie ma sensu bez byciaAnimal
.Z drugiej strony interfejsy to inna historia. Nie używają dziedziczenia, ale zapewniają polimorfizm (który można również zaimplementować z dziedziczeniem). Nie modelują związku „ jest” , ale w większym stopniu obsługuje .
Weźmy np.
IComparable
- obiekt, który umożliwia porównanie z innym .Funkcjonalność klasy nie zależy od implementowanych interfejsów, interfejs zapewnia jedynie ogólny sposób dostępu do tej funkcji. Moglibyśmy wciąż
Dispose
tematyceGraphics
lubFileStream
bez konieczności ich implementacjiIDisposable
. Interfejs tylko odnosi się do sposobów ze sobą.Zasadniczo można dodawać i usuwać interfejsy podobnie jak rozszerzenia bez zmiany zachowania klasy, po prostu wzbogacając dostęp do niej. Nie możesz jednak zabrać klasy podstawowej, ponieważ obiekt stałby się bez znaczenia!
źródło
Animal
streszczenie. Umożliwia to pisanieabstract
funkcji specjalizowanych przez klasy pochodne. Lub więcej w zakresie programowania - to jest chyba nie ma sensu, aby utworzyćUserControl
, ale jakButton
toUserControl
, więc mamy ten sam rodzaj relacji klasa / klasy bazowej.Właśnie zdałem sobie sprawę, że od lat nie korzystałem z klas abstrakcyjnych (przynajmniej nie stworzonych).
Klasa abstrakcyjna to dość dziwna bestia między interfejsem a implementacją. W klasach abstrakcyjnych jest coś, co mrowi mój zmysł pająka. Twierdziłbym, że nie należy „ujawniać” implementacji za interfejsem w żaden sposób (ani dokonywać żadnych powiązań).
Nawet jeśli istnieje potrzeba wspólnego zachowania między klasami implementującymi interfejs, użyłbym raczej niezależnej klasy pomocniczej niż klasy abstrakcyjnej.
Wybrałbym interfejsy.
źródło
IMHO, myślę, że tutaj jest mieszanie pojęć.
Klasy używają pewnego rodzaju dziedziczenia, podczas gdy interfejsy używają innego rodzaju dziedziczenia.
Oba rodzaje dziedziczenia są ważne i przydatne, a każdy z nich ma swoje zalety i wady.
Zacznijmy od początku.
Historia z interfejsami zaczyna się dawno temu w C ++. W połowie lat 80., kiedy opracowano C ++, idea interfejsów jako innego rodzaju nie została jeszcze zrealizowana (przyjdzie z czasem).
C ++ miał tylko jeden rodzaj dziedziczenia, rodzaj dziedziczenia używany przez klasy, zwany dziedziczeniem implementacji.
Aby móc zastosować zalecenie GoF Book: „Aby zaprogramować interfejs, a nie implementację”, C ++ obsługiwał klasy abstrakcyjne i wielokrotne dziedziczenie implementacji, aby móc wykonywać interfejsy w języku C # (i przydatne!) .
C # wprowadza nowy rodzaj, interfejsy i nowy rodzaj dziedziczenia, dziedziczenie interfejsu, aby oferować co najmniej dwa różne sposoby obsługi „Programowania interfejsu, a nie implementacji”, z jednym ważnym zastrzeżeniem: tylko C # obsługuje wielokrotne dziedziczenie z dziedziczeniem interfejsu (a nie z dziedziczeniem implementacji).
Architektonicznymi powodami interfejsów w języku C # jest więc zalecenie GoF.
Mam nadzieję, że to może pomóc.
Z pozdrowieniami, Gastón
źródło
Zastosowanie metod rozszerzenia do interfejsu jest przydatne do zastosowania wspólnego zachowania w klasach, które mogą mieć tylko wspólny interfejs. Takie klasy mogą już istnieć i mogą być zamknięte na rozszerzenia za pomocą innych środków.
W przypadku nowych projektów wolałbym stosować metody rozszerzeń na interfejsach. W przypadku hierarchii typów metody rozszerzenia umożliwiają przedłużenie zachowania bez konieczności otwierania całej hierarchii w celu modyfikacji.
Jednak metody rozszerzeń nie mogą uzyskać dostępu do prywatnej implementacji, więc na szczycie hierarchii, jeśli muszę obudować prywatną implementację za interfejsem publicznym, klasa abstrakcyjna byłaby jedynym sposobem.
źródło
Myślę, że w C # wielokrotne dziedziczenie nie jest obsługiwane i dlatego koncepcja interfejsu została wprowadzona w C #.
W przypadku klas abstrakcyjnych wielokrotne dziedziczenie również nie jest obsługiwane. Łatwo jest więc zdecydować się na użycie abstrakcyjnych klas lub interfejsów.
źródło
Jestem w trakcie wdrażania klasy Abstract w moim projekcie po prostu dlatego, że C # nie obsługuje operatorów (w moim przypadku niejawnych konwersji) na interfejsach.
źródło