Całkiem proste. Implementuję interfejs, ale jest jedna właściwość, która jest niepotrzebna dla tej klasy i w rzeczywistości nie powinna być używana. Mój początkowy pomysł polegał na zrobieniu czegoś takiego:
int IFoo.Bar
{
get { raise new NotImplementedException(); }
}
Przypuszczam, że nie ma w tym nic złego, ale nie wydaje się to „właściwe”. Czy ktoś wcześniej spotkał się z podobną sytuacją? Jeśli tak, to jak do tego podszedłeś?
c#
design
interfaces
implementations
Chris Pratt
źródło
źródło
Odpowiedzi:
Jest to klasyczny przykład tego, jak ludzie decydują się na naruszenie zasady substytucji Liskowa. Zdecydowanie odradzam, ale zachęciłbym do ewentualnie innego rozwiązania:
Jeśli pierwszy przypadek dotyczy Ciebie, po prostu nie implementuj interfejsu dla tej klasy. Potraktujcie to jak gniazdka elektrycznego, gdzie dziura ziemia jest niepotrzebny, więc nie rzeczywiście dołączyć do ziemi. Nie podłączasz niczego z uziemieniem i nic wielkiego! Ale gdy tylko użyjesz czegoś, co wymaga gruntu - możesz być w spektakularnej porażce. Lepiej nie dziurawić fałszywej dziury. Więc jeśli twoja klasa nie robi właściwie tego, co zamierza interfejs, nie implementuj interfejsu.
Oto kilka szybkich bitów z wikipedii:
Zasadę podstawienia Liskowa można po prostu sformułować w następujący sposób: „Nie wzmacniaj warunków wstępnych i nie osłabiaj warunków wstępnych”.
Aby uzyskać semantyczną interoperacyjność i zastępowalność między różnymi realizacjami tych samych umów - potrzebujesz ich wszystkich, aby zobowiązały się do tych samych zachowań.
Zasada segregacji interfejsów mówi, że interfejsy powinny być podzielone na spójne zestawy, tak że nie potrzebujesz interfejsu, który robi wiele różnych czynności, gdy potrzebujesz tylko jednego obiektu. Pomyśl jeszcze raz o interfejsie gniazda elektrycznego, może również mieć termostat, ale utrudniłoby to zainstalowanie gniazda elektrycznego i może utrudnić korzystanie z niego do celów innych niż ogrzewanie. Podobnie jak gniazdko elektryczne z termostatem, duże interfejsy są trudne do wdrożenia i trudne w użyciu.
źródło
Dla mnie wygląda dobrze, jeśli to twoja sytuacja.
Wydaje mi się jednak, że twój interfejs (lub jego użycie) jest zepsuty, jeśli klasa wyprowadzająca nie implementuje go w całości. Rozważ podzielenie tego interfejsu.
Oświadczenie: Wymaga to wielokrotnego dziedziczenia, aby wykonać poprawnie, i nie mam pojęcia, czy C # obsługuje to.źródło
Natknąłem się na tę sytuację. W rzeczywistości, jak wskazano w innym miejscu, BCL ma takie przypadki ... Postaram się podać lepsze przykłady i podać uzasadnienie:
Gdy masz już dostarczony interfejs, który przechowujesz ze względu na kompatybilność i ...
Interfejs zawiera przestarzałe lub zdyskredytowane elementy. Na przykład
BlockingCollection<T>.ICollection.SyncRoot
(między innymi), choć sam w sobieICollection.SyncRoot
nie jest przestarzały, będzie rzucałNotSupportedException
.Interfejs zawiera elementy, które są udokumentowane jako opcjonalne i które implementacja może zgłosić wyjątek. Na przykład w MSDN, co do
IEnumerator.Reset
tego mówi:Przez pomyłkę w konstrukcji interfejsu powinien być przede wszystkim więcej niż jeden interfejs. W BCL jest powszechnym wzorem do implementacji wersji kontenerów tylko do odczytu
NotSupportedException
. Zrobiłem to sam, to czego oczekuje teraz ... robięICollection<T>.IsReadOnly
zwrottrue
więc można powiedzieć im appart. Prawidłowym projektem byłoby mieć czytelną wersję interfejsu, a następnie dziedziczy pełny interfejs.Nie ma lepszego interfejsu do użycia. Na przykład mam klasę, która pozwala na dostęp do elementów według indeksu, sprawdzanie, czy zawiera element i na jakim indeksie ma pewien rozmiar, możesz skopiować go do tablicy ... wydaje się, że to zadanie,
IList<T>
ale moja klasa ma stały rozmiar i nie obsługuje dodawania ani usuwania, więc działa bardziej jak tablica niż lista. Ale nie ma tegoIArray<T>
w BCL .Interfejs należy do interfejsu API, który jest przeniesiony na wiele platform, a przy implementacji konkretnej platformy niektóre jej części nie są obsługiwane. Idealnie byłoby istnieć jakiś sposób, aby to wykryć, aby przenośny kod korzystający z takiego API mógł decydować o tym, czy wywołać te części, ale jeśli je wywołasz, to jest całkowicie właściwe
NotSupportedException
. Jest to szczególnie prawdziwe, jeśli jest to port nowej platformy, który nie był przewidziany w pierwotnym projekcie.Zastanów się także, dlaczego nie jest obsługiwane?
Czasami
InvalidOperationException
jest lepszym rozwiązaniem. Na przykład jeszcze jednym sposobem na dodanie polimorfizmu do klasy jest posiadanie różnych implementacji interfejsu wewnętrznego, a twój kod wybiera, który z nich utworzyć w zależności od parametrów podanych w konstruktorze klasy. [Jest to szczególnie przydatne, jeśli wiesz, że zestaw opcji jest stały i nie chcesz zezwalać na wprowadzanie klas stron trzecich przez wstrzykiwanie zależności.] Zrobiłem to, aby backportować ThreadLocal, ponieważ implementacja śledzenia i śledzenia nie jest zbyt daleko, a coThreadLocal.Values
rzuca się na implementację bez śledzenia?InvalidOperationException
nawet jeśli nie zależy to od stanu obiektu. W tym przypadku sam przedstawiłem klasę i wiedziałem, że ta metoda musi zostać zaimplementowana przez zgłoszenie wyjątku.Czasami wartość domyślna ma sens. Na przykład we
ICollection<T>.IsReadOnly
wspomnianym powyżej sensownym jest zwrócenie „prawdy” lub „fałszu” w zależności od przypadku. Więc ... co to jest semantykaIFoo.Bar
? może być jakaś sensowna wartość domyślna do zwrócenia.Dodatek: jeśli masz kontrolę nad interfejsem (i nie musisz pozostać przy nim dla kompatybilności), nie powinno być przypadku, w którym musisz rzucić
NotSupportedException
. Chociaż może być konieczne podzielenie interfejsu na dwa lub więcej mniejszych interfejsów, aby mieć odpowiednie dopasowanie do twojej skrzynki, co może prowadzić do „zanieczyszczenia” w ekstremalnych sytuacjach.źródło
Tak, zrobili to projektanci bibliotek .Net. Klasa Dictionary robi to, co robisz. Wykorzystuje jawną implementację, aby skutecznie ukryć * niektóre metody IDictionary. Jest to wyjaśnione lepiej tutaj , ale do podsumowania, w celu wykorzystania dodać słownik'S, CopyTo lub usunąć metodami, które mają KeyValuePairs, najpierw trzeba do oddania obiektu do IDictionary.
* To nie „ukrywanie” metod w ścisłym znaczeniu słowa „ukryj”, tak jak używa tego Microsoft . Ale w tym przypadku nie znam lepszego terminu.
źródło
Zawsze możesz po prostu zaimplementować i zwrócić wartość łagodną lub domyślną. W końcu to tylko własność. Może zwrócić 0 (domyślna właściwość int) lub dowolną wartość, która ma sens w twojej implementacji (int.MinValue, int.MaxValue, 1, 42 itd.)
Zgłoszenie wyjątku wydaje się złą formą.
źródło