Używam interfejsów rzadko i uważam je za wspólne w innych kodach.
Poza tym w kodzie rzadko tworzę podklasy i superklasy (tworząc własne klasy).
- Czy to źle?
- Czy sugerowałbyś zmianę tego stylu?
- Czy ten styl ma jakieś skutki uboczne?
- Czy to dlatego, że nie pracowałem przy dużych projektach?
programming-practices
coding-style
interfaces
jineesh joseph
źródło
źródło
Odpowiedzi:
Istnieje kilka powodów, dla których warto używać interfejsów:
http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx
Interfejsy są jak wszystko inne w programowaniu. Jeśli ich nie potrzebujesz, nie używaj ich. Widziałem je szeroko stosowane ze względu na styl, ale jeśli nie potrzebujesz specjalnych właściwości i możliwości, które zapewnia interfejs, nie widzę korzyści z używania ich „tylko dlatego, że”.
źródło
Zarówno dziedziczenie klas, jak i interfejsy mają swoje miejsce. Dziedziczenie oznacza „jest”, podczas gdy interfejs zawiera umowę określającą, jak coś „zachowuje się”.
Powiedziałbym, że częstsze używanie interfejsów wcale nie jest złą praktyką. Obecnie czytam „Skuteczne C # - 50 konkretnych sposobów na poprawę twojego C #” Billa Wagnera. W pozycji nr 22 znajduje się cytat „Wolę definiowanie i wdrażanie interfejsów niż dziedziczenie”.
Zasadniczo używam klas podstawowych, gdy muszę zdefiniować konkretną implementację typowego zachowania między typami pokrewnymi. Częściej korzystam z interfejsów. W rzeczywistości zwykle zaczynam od zdefiniowania interfejsu dla klasy, kiedy zaczynam ją tworzyć ... nawet jeśli ostatecznie nie skompiluję interfejsu, stwierdzę, że pomaga to od zdefiniowania publicznego interfejsu API klasa od samego początku. Jeśli stwierdzę, że mam wiele klas, które zarówno implementują interfejs, a logika implementacji jest identyczna, dopiero wtedy zadam sobie pytanie, czy sensowne byłoby wdrożenie wspólnej klasy podstawowej między typami.
Kilka cytatów z książki Billa Wagnersa ...
wszystkie klasy pochodne natychmiast uwzględniają to zachowanie. Dodanie elementu do interfejsu powoduje uszkodzenie wszystkich klas, które implementują ten interfejs. Nie będą zawierać nowej metody i nie będą się już kompilować. Każdy implementator musi zaktualizować ten typ, aby uwzględnić nowego członka. Wybór między abstrakcyjną klasą bazową a interfejsem to pytanie, jak najlepiej wspierać swoje abstrakcje w czasie. Interfejsy są naprawione: interfejs wydajesz jako umowę dotyczącą zestawu funkcji, które może zaimplementować każdy typ. Klasy podstawowe mogą być z czasem przedłużane. Rozszerzenia te stają się częścią każdej klasy pochodnej. Oba modele mogą być mieszane w celu ponownego wykorzystania kodu implementacji przy jednoczesnym wsparciu wielu interfejsów. ” Nie będą zawierać nowej metody i nie będą się już kompilować. Każdy implementator musi zaktualizować ten typ, aby uwzględnić nowego członka. Wybór między abstrakcyjną klasą bazową a interfejsem to pytanie, jak najlepiej wspierać swoje abstrakcje w czasie. Interfejsy są naprawione: interfejs wydajesz jako umowę dotyczącą zestawu funkcji, które może zaimplementować każdy typ. Klasy podstawowe mogą być z czasem przedłużane. Rozszerzenia te stają się częścią każdej klasy pochodnej. Oba modele mogą być mieszane w celu ponownego wykorzystania kodu implementacji przy jednoczesnym wsparciu wielu interfejsów. ” Nie będą zawierać nowej metody i nie będą się już kompilować. Każdy implementator musi zaktualizować ten typ, aby uwzględnić nowego członka. Wybór między abstrakcyjną klasą bazową a interfejsem to pytanie, jak najlepiej wspierać swoje abstrakcje w czasie. Interfejsy są naprawione: interfejs wydajesz jako umowę dotyczącą zestawu funkcji, które może zaimplementować każdy typ. Klasy podstawowe mogą być z czasem przedłużane. Rozszerzenia te stają się częścią każdej klasy pochodnej. Oba modele mogą być mieszane w celu ponownego wykorzystania kodu implementacji przy jednoczesnym wsparciu wielu interfejsów. ” Wydajesz interfejs jako umowę dotyczącą zestawu funkcji, które może zaimplementować każdy typ. Klasy podstawowe mogą być z czasem przedłużane. Rozszerzenia te stają się częścią każdej klasy pochodnej. Oba modele mogą być mieszane w celu ponownego wykorzystania kodu implementacji przy jednoczesnym wsparciu wielu interfejsów. ” Wydajesz interfejs jako umowę dotyczącą zestawu funkcji, które może zaimplementować każdy typ. Klasy podstawowe mogą być z czasem przedłużane. Rozszerzenia te stają się częścią każdej klasy pochodnej. Oba modele mogą być mieszane w celu ponownego wykorzystania kodu implementacji przy jednoczesnym wsparciu wielu interfejsów. ”
„Interfejsy kodowania zapewniają większą elastyczność innym programistom niż kodowanie do typów klas podstawowych”.
„Używanie interfejsów do definiowania interfejsów API dla klasy zapewnia większą elastyczność”.
„Gdy Twój typ udostępnia właściwości jako typy klas, udostępnia cały interfejs dla tej klasy. Korzystając z interfejsów, możesz ujawnić tylko metody i właściwości, których mają używać klienci.”
„Klasy podstawowe opisują i wdrażają typowe zachowania w pokrewnych konkretnych typach. Interfejsy opisują atomowe elementy funkcjonalności, które mogą być implementowane przez niepowiązane konkretne typy. Obie mają swoje miejsce. Klasy definiują tworzone typy. Interfejsy opisują zachowanie tych typów jako elementy funkcjonalności. Jeśli zrozumiesz różnice, stworzysz bardziej wyraziste projekty, które będą bardziej odporne na zmiany. Użyj hierarchii klas, aby zdefiniować typy pokrewne. Ujawnij funkcjonalność za pomocą interfejsów zaimplementowanych we wszystkich tych typach. ”
źródło
Jedną z rzeczy, o których nie wspomniano, jest testowanie: we wszystkich bibliotekach próbnych języka C #, a także w Javie, klas nie można wyśmiewać, chyba że implementują interfejs. Prowadzi to wiele projektów zgodnie z praktykami Agile / TDD, aby nadać każdej klasie własny interfejs.
Niektórzy ludzie uważają tę najlepszą praktykę, ponieważ „zmniejsza ona sprzężenie”, ale nie zgadzam się - myślę, że jest to tylko obejście niedoboru języka.
Myślę, że interfejsów najlepiej używać, gdy masz dwie lub więcej klas, które abstrakcyjnie wykonują „to samo”, ale na różne sposoby.
Na przykład .NET Framework ma wiele klas, które przechowują listy rzeczy , ale wszystkie przechowują te rzeczy na różne sposoby. Dlatego sensowne jest posiadanie abstrakcyjnego
IList<T>
interfejsu, który można wdrożyć za pomocą różnych metod.Powinieneś go również użyć, jeśli chcesz, aby klasy 2+ były wymienne lub wymienne w przyszłości. Jeśli w przyszłości pojawi się nowy sposób przechowywania list
AwesomeList<T>
, to zakładając, że używaszIList<T>
całego kodu, zmiana go na użycieAwesomeList<T>
oznaczałaby zmianę kilkudziesięciu wierszy, a nie kilkuset / tysięcy.źródło
Głównym skutkiem niestosowania dziedziczenia i odpowiednich interfejsów jest ścisłe połączenie . To może być trochę trudne do nauczenia się rozpoznawać, ale zwykle najbardziej oczywistym objawem jest to, że po dokonaniu zmiany często musisz przejrzeć całą masę innych plików, aby wprowadzić zmiany w wyniku tętnienia.
źródło
Nie, wcale nie jest źle. Należy stosować jawne interfejsy, wtedy i tylko wtedy, gdy dwie klasy ze wspólnym interfejsem muszą być wymienne w czasie wykonywania. Jeśli nie muszą być wymienne, nie dziedziczą. To takie proste. Dziedziczenie jest kruche i powinno się go unikać w miarę możliwości. Jeśli możesz tego uniknąć lub zamiast tego użyć leków ogólnych, zrób to.
Problem polega na tym, że w językach takich jak C # i Java ze słabymi generycznymi czasami kompilacji możesz w końcu naruszyć DRY, ponieważ nie ma sposobu na napisanie jednej metody, która poradziłaby sobie z więcej niż jedną klasą, chyba że wszystkie klasy odziedziczą z tej samej bazy. C # 4
dynamic
mogą sobie z tym poradzić.Rzecz w tym, że dziedziczenie jest jak zmienne globalne - kiedy go dodasz, a twój kod będzie od niego zależał, Boże, pomóż mu go zabrać. Można go jednak dodać w dowolnym momencie, a nawet dodać bez zmiany klasy podstawowej za pomocą pewnego rodzaju opakowania.
źródło
Tak, nie (lub zbyt rzadko) używanie interfejsów jest prawdopodobnie złą rzeczą. Interfejsy (w sensie abstrakcyjnym, a konstrukcja języka C # / Java jest całkiem dobrym przybliżeniem tego abstrakcyjnego sensu) definiują wyraźne punkty interakcji między systemami i podsystemami. Pomaga to zmniejszyć sprzężenie i sprawia, że system jest łatwiejszy w utrzymaniu. Jak wszystko, co poprawia łatwość konserwacji, staje się tym ważniejsze, im większy jest system.
źródło
Nie używałem interfejsu od lat. Oczywiście dzieje się tak, ponieważ programuję prawie wyłącznie w Erlangu od lat i cała koncepcja interfejsu po prostu nie istnieje. (Najbliższe jest „zachowanie” i tak naprawdę nie jest to to samo, chyba że zmrużysz oczy naprawdę i nie spojrzysz na nie kątem oka.)
Tak naprawdę twoje pytanie jest zależne od paradygmatu (w tym przypadku OOP), a ponadto jest bardzo zależne od języka (istnieją języki OOP bez interfejsów).
źródło
Jeśli mówisz o korzystaniu z Javy, jednym z powodów korzystania z interfejsów jest to, że umożliwiają one używanie obiektów proxy bez bibliotek do generowania kodu. Może to być znaczną zaletą, gdy pracujesz ze złożonymi ramami, takimi jak Spring. Co więcej, niektóre funkcje wymagają interfejsów: RMI jest klasycznym przykładem tego, ponieważ musisz opisać funkcjonalność, którą udostępniasz, w kategoriach interfejsów (które dziedziczą
java.rmi.Remote
), niezależnie od tego, czy je wdrażasz.źródło
Rzeczy się zmieniają ( MS Moles ), ale głównym powodem, dla którego uważam, że kodowanie prawie wyłącznie do interfejsów jest dobre, jest to, że łatwo się z nich kpić i naturalnie pasują do architektury IoC.
IMO powinieneś zawsze pracować tylko z interfejsami lub całkowicie głupimi DAO tam, gdzie to możliwe. Gdy przejdziesz do tego sposobu myślenia i zaczniesz korzystać z biblioteki, która nie ujawnia się przez interfejsy i robi wszystko za pomocą konkretnych obiektów, muszę być szczery, wszystko wydaje się trochę niezgrabne.
źródło
W przypadku starych projektów modowych używa się interfejsów, aby uniknąć odwołań cyklicznych, ponieważ odniesienia długoterminowe mogą stać się poważnym problemem w zakresie konserwacji na dłuższą metę.
Zły:
Nie jest zły:
Zwykle A, B, PartOfClassB_AccessedByA zaimplementowane z osobnymi plikami.
źródło
Programowanie oparte na interfejsie pomaga uczynić kod bardziej elastycznym i łatwiejszym do testowania. Klasy implementacji można zmienić bez dotykania kodu klienta [Elastyczność]. Podczas testowania kodu można zastąpić faktyczne programowanie oparte na interfejsie, co czyni kod bardziej elastycznym i łatwiejszym do testowania. Klasy implementacji można zmienić bez dotykania kodu klienta [Elastyczność]. Podczas testowania kodu można zastąpić rzeczywisty obiekt próbnymi obiektami [Testability] .bject obiektami próbnymi [Testability].
źródło