Jest jedna drobna różnica między Javą a C #, która jest tutaj istotna. W Javie domyślnie każdy członek jest wirtualny. W języku C # każdy element jest domyślnie zaplombowany - z wyjątkiem elementów interfejsu.
Założenia, które się z tym wiążą, mają wpływ na wytyczną - w Javie każdy typ publiczny powinien być uważany za niekończący, zgodnie z zasadą substytucji Liskowa [1]. Jeśli masz tylko jedną implementację, nazwiesz klasę Parser; jeśli uznasz, że potrzebujesz wielu implementacji, po prostu zmienisz klasę na interfejs o tej samej nazwie i zmienisz nazwę konkretnej implementacji na coś opisowego.
W języku C # głównym założeniem jest to, że kiedy dostajesz klasę (nazwa nie zaczyna się od I), to jest klasa, którą chcesz. Pamiętaj, że nie jest to w przybliżeniu 100% dokładne - typowym kontrprzykładem byłyby takie klasy Stream(które naprawdę powinien byłby być interfejsem lub kilkoma interfejsami), a każdy ma swoje własne wytyczne i pochodzenie z innych języków. Istnieją również inne wyjątki, takie jak dość powszechnie stosowany Baseprzyrostek oznaczający klasę abstrakcyjną - podobnie jak w przypadku interfejsu, wiesz, że typ powinien być polimorficzny.
Istnieje również przyjemna funkcja użyteczności polegająca na pozostawieniu nazwy bez prefiksu I dla funkcjonalności związanej z tym interfejsem bez konieczności uciekania się do tworzenia interfejsu jako klasy abstrakcyjnej (co zaszkodziłoby z powodu braku wielokrotnego dziedziczenia klas w języku C #). Zostało to spopularyzowane przez LINQ, który wykorzystuje IEnumerable<T>jako interfejs i Enumerablejako repozytorium metod mających zastosowanie do tego interfejsu. Nie jest to konieczne w Javie, gdzie interfejsy mogą również zawierać implementacje metod.
Ostatecznie Iprefiks jest szeroko stosowany w świecie C #, a przez to w świecie .NET (ponieważ zdecydowanie większość kodu .NET jest napisana w języku C #, warto stosować się do wytycznych C # dla większości publicznych interfejsów). Oznacza to, że prawie na pewno będziesz pracować z bibliotekami i kodem, który jest zgodny z tą notacją, i warto przyjąć tradycję, aby zapobiec niepotrzebnym nieporozumieniom - to nie tak, że pominięcie prefiksu poprawi kod :)
Zakładam, że rozumowanie wuja Boba było takie:
IBananajest abstrakcyjnym pojęciem banana. Jeśli może istnieć jakaś klasa implementująca, która nie miałaby lepszej nazwy niż Banana, abstrakcja jest całkowicie pozbawiona znaczenia i powinieneś porzucić interfejs i po prostu użyć klasy. Jeśli istnieje lepsza nazwa (powiedzmy LongBananalub AppleBanana), nie ma powodu, aby nie używać jej Bananajako nazwy interfejsu. Dlatego użycie Iprzedrostka oznacza, że masz niepotrzebną abstrakcję, co sprawia, że kod jest trudniejszy do zrozumienia bez żadnych korzyści. A ponieważ ścisłe OOP sprawi, że zawsze będziesz kodować przeciwko interfejsom, jedynym miejscem, gdzie nie zobaczysz Iprefiksu na typie, będzie konstruktor - dość bezsensowny hałas.
Jeśli zastosujesz to do przykładowego IParserinterfejsu, możesz wyraźnie zobaczyć, że abstrakcja znajduje się całkowicie na terytorium „bez znaczenia”. Albo jest coś specyficznego o konkretnej implementacji parsera (np JsonParser, XmlParser...), czy też po prostu użyć klasy. Nie ma czegoś takiego jak „domyślna implementacja” (chociaż w niektórych środowiskach rzeczywiście ma to sens - zwłaszcza COM), albo istnieje konkretna implementacja, albo potrzebujesz abstrakcyjnej metody klasy lub rozszerzenia dla „domyślnych”. Jednak w języku C #, chyba że twoja baza kodu już pomija Iprefiks -f, zachowaj go. Po prostu zanotuj za każdym razem, gdy zobaczysz kod podobny class Something: ISomething- oznacza to, że ktoś nie jest zbyt dobry w przestrzeganiu YAGNI i tworzeniu rozsądnych abstrakcji.
[1] - Technicznie rzecz biorąc, nie jest to specjalnie wspomniane w pracy Liskova, ale jest to jedna z podstaw oryginalnej pracy OOP i podczas mojego czytania Liskova nie zakwestionowała tego. W mniej ścisłej interpretacji (tej przyjętej przez większość języków OOP) oznacza to, że każdy kod używający typu publicznego, który jest przeznaczony do podstawienia (tj. Nie-końcowy / zapieczętowany) musi działać z dowolną zgodną implementacją tego typu.