Czy nazwy interfejsów powinny zaczynać się od prefiksu „I”?

73

Czytam „ Czysty kod ” Roberta Martina, aby, mam nadzieję, zostać lepszym programistą. Chociaż jak dotąd żaden z nich nie był przełomowy, zmusił mnie do odmiennego myślenia o sposobie projektowania aplikacji i pisania kodu.

Jest jedna część książki, z którą nie tylko się nie zgadzam, ale nie ma dla mnie sensu, szczególnie w odniesieniu do konwencji nazewnictwa interfejsów. Oto tekst zaczerpnięty bezpośrednio z książki. Pogrubiłem ten aspekt, który wydaje mi się mylący i chciałbym wyjaśnić.

Wolę pozostawić interfejsy bez ozdób. Poprzednie I, tak powszechne w dzisiejszych legendarnych brodzikach, jest w najlepszym razie rozproszeniem, aw najgorszym przypadku zbyt dużą ilością informacji. Nie chcę, aby użytkownicy wiedzieli, że przekazuję im interfejs .

Może dlatego, że jestem tylko studentem, a może dlatego, że nigdy nie zajmowałem się programowaniem zawodowym lub zespołowym, ale chciałbym, aby użytkownik wiedział, że jest to interfejs. Istnieje duża różnica między implementacją interfejsu a rozszerzeniem klasy.

Moje pytanie sprowadza się więc do: „Dlaczego powinniśmy ukrywać fakt, że jakaś część kodu oczekuje interfejsu?”

Edytować

W odpowiedzi na odpowiedź:

Jeśli Twój typ to interfejs lub klasa to Twoja firma, a nie osoba korzystająca z Twojego kodu. Dlatego nie powinieneś ujawniać szczegółów swojego kodu w tym kodzie strony trzeciej.

Dlaczego nie powinienem „ujawniać” szczegółów tego, czy dany typ jest interfejsem czy klasą dla kodu strony trzeciej? Czy to nie jest ważne, aby zewnętrzny programista korzystający z mojego kodu wiedział, czy będą implementować interfejs, czy rozszerzać klasę? Czy różnice po prostu nie są tak ważne, jak chcę, żeby były w mojej pamięci?

Charles Sprayberry
źródło
3
Zgadzam się z twoim punktem widzenia. Jest moment, w którym ukrywanie zbyt dużej ilości informacji nie jest zbyt pomocne. Jednak nawet jeśli zastosujesz się do tych wskazówek, nadal będziesz w stanie powiedzieć typ za pomocą IDE lub dodatku.
NoChance
3
To pytanie jest zasadniczo znane jako pytanie „notacja węgierska”, powinieneś znaleźć wiele argumentów i powód, dla którego większość programistów spoza MS porzuciło je pod tym słowem kluczowym. Notacja węgierska dominowała głównie w przypadku zmiennych, ale w zasadzie była taka sama w przypadku typów.
thiton
2
Poprzednia edycja tytułu była okropna. To pytanie nie dotyczy ogólnie notacji węgierskiej tylko dlatego, że wymienia konwencję, która może być z nią powiązana. Względne zalety HN są tutaj całkowicie nieistotne; pytanie dotyczyło w szczególności interfejsów i klas oraz tego, czy różnice semantyczne są wystarczająco ważne / interesujące, aby uzasadnić konwencję nazewnictwa specjalnych przypadków.
Aaronaught
Re to know whether they will be implementing an interface or extending a class: tak, ale większość użytkowników twojego kodu wywoła go, nie zaimplementuje ani nie rozszerzy, i naprawdę nie obchodzi go, co to jest.
david.pfx
Ze względu na swoją wartość zdecydowanie wolę prefiks „I”. Z tego samego powodu używam również przedrostka „Streszczenie” w klasach abstrakcyjnych. Nie robi to różnicy dla konsumentów klasy / interfejsu, ale może mieć dużą różnicę dla tych, którzy muszą dostarczyć jego instancje, a także znacznie ułatwia innym programistom, którzy czytają Twój kod. Oznacza to, że mogą zobaczyć na pierwszy rzut oka, z czym mają do czynienia, zamiast konieczności skonsultowania się z IDE dla poszczególnych przypadków, aby uzyskać więcej informacji. Właśnie zacząłem używać Angulara i naprawdę denerwuje mnie to, że nie przestrzegają tej konwencji!
Dan King

Odpowiedzi:

66

Jeśli przestaniesz o tym myśleć, zobaczysz, że interfejs tak naprawdę nie różni się znacznie semantycznie od klasy abstrakcyjnej:

  • Obie mają metody i / lub właściwości (zachowanie);
  • Żaden nie powinien mieć nieprywatnych pól (danych);
  • Żadnej z nich nie można utworzyć bezpośrednio;
  • Pochodzenie od jednego oznacza implementowanie wszelkich metod abstrakcyjnych, które posiada, chyba że typ pochodny jest również abstrakcyjny.

W rzeczywistości najważniejsze różnice między klasami i interfejsami to:

  • Interfejsy nie mogą mieć prywatnych danych;
  • Członkowie interfejsu nie mogą mieć modyfikatorów dostępu (wszyscy członkowie są „publiczni”);
  • Klasa może implementować wiele interfejsów (w przeciwieństwie do możliwości dziedziczenia tylko z jednej klasy bazowej).

Ponieważ jedyne szczególnie znaczące rozróżnienia między klasami i interfejsami dotyczą (a) prywatnych danych i (b) hierarchii typów - z których żadne nie robi najmniejszej różnicy dla osoby wywołującej - generalnie nie trzeba wiedzieć, czy typ jest interfejsem czy Klasa. Na pewno nie potrzebujesz wizualnego wskazania.

Istnieją jednak pewne narożne przypadki, o których należy pamiętać. W szczególności, jeśli używasz refleksji, przechwytywania, dynamicznych serwerów proxy / miksów, tkania kodu bajtowego, generowania kodu lub czegokolwiek, co wiąże się z bałaganem bezpośrednio z systemem pisania w środowisku lub samym kodem - wtedy jest to bardzo pomocne, a czasem konieczne, aby wiedzieć od razu bat, czy masz do czynienia z interfejsem czy klasą. Wyraźnie nie chcesz, aby Twój kod w tajemniczy sposób zawiódł, ponieważ próbowałeś dodać klasę zamiast interfejsu jako mixin.

Jednak w przypadku typowego, waniliowego, codziennego kodu logiki biznesowej rozróżnienia między klasami abstrakcyjnymi a interfejsami nie muszą być reklamowane, ponieważ nigdy nie wejdą w grę.

Biorąc to wszystko pod uwagę, staram się i tak prefiksować moje interfejsy C #, Iponieważ jest to konwencja .NET używana i zalecana przez Microsoft. A kiedy wyjaśniam konwencje kodowania nowemu programistowi, znacznie łatwiej jest po prostu korzystać z reguł Microsoftu niż wyjaśniać, dlaczego mamy własne „specjalne” reguły.

Aaronaught
źródło
Dziękuję za ten podział. +1 za „znaczące rozróżnienie klas i interfejsów ...”
Charles Sprayberry
23
+1 za „Wszystko to powiedziawszy, i tak mam tendencję do prefiksu moich interfejsów C # z I, ponieważ jest to konwencja .NET używana i zalecana przez Microsoft”. To dla mnie wystarczający powód w C #. Przestrzegając tego wspólnego standardu, bardziej prawdopodobne jest, że inni programiści .NET zidentyfikują interfejs.
Robotsushi,
4
Ponieważ ktoś, kto pisze zarówno Java, jak i C #, oświadczenie „Wszystko to jest powiedziane, i tak mam tendencję do prefiksu moich interfejsów C # i tak, ponieważ jest to konwencja .NET używana i zalecana przez Microsoft” nie może być lekceważona - jest to jedna z rzeczy, które pomaga mi zapamiętać, w którym języku pracuję,
Aidos
3
Inną różnicą w .NET (ale nie w Javie) jest to, że wiele języków .NET nie pozwala interfejsom zawierać metod statycznych, więc zhakowanie Iinterfejsu może dać dobrą nazwę dla klasy przechowującej metody statyczne powiązane z interfejsem (np. Enumerable<T>.Emptylub Comparer<T>.Default).
supercat
Mam jeden problem. W C ++, jeśli otworzysz nagłówek i zobaczysz, że class Foojuż się rozszerza class Bar, nie jest od razu oczywiste, czy class Barjest rozszerzany czy implementowany. Musisz teraz zajrzeć do class Barnagłówka, aby zdecydować, czy można zmodyfikować class Foorozszerzenie class Baz. Co więcej, prefiks I daje oczywistą wizualną wskazówkę, czy „pojedyncza klasa bazowa” jest naruszana, czy nie - więc za każdym razem, gdy otwierasz nagłówek, otrzymujesz kontrolę poczytalności.
jsj
14

Pod wieloma względami spójność jest ważniejsza niż konwencja. Dopóki jesteś konsekwentny w swoich schematach nazewnictwa, nie będzie trudno z nimi pracować. Przedrostek łączy się z I, jeśli chcesz, lub po prostu pozostaw nazwę bez ozdoby, nie ma to dla mnie znaczenia, dopóki wybierzesz styl i pozostanie przy nim!

Jim Nutt
źródło
2
+1 za spójność> konwencja. W języku C # prefiks miałby literę I, aw języku Java nie. Różne zadania wymagają różnych konwencji
Bringer128,
2
+1, podczas gdy ja osobiście wolałbym nie mieć Is na moich interfejsach, niespójność z BCL i bibliotekami stron trzecich używanymi w projektach .Net nie jest opcją imho.
jk.
13

Książka jest pełna dobrych rzeczy, ale nadal dodawałbym „I” do nazwy interfejsu.

Wyobraź sobie, że masz public interface ILogdomyślną implementację public class Log.

Teraz, jeśli zdecydujesz się na to public interface Log, nagle musisz zmienić klasę Log na public class LogDefaultlub coś w tych liniach. Z jednej dodatkowej postaci przeszliście na siedem - to z pewnością marnotrawstwo.

Często istnieje cienka granica między teorią a praktyką. Teoretycznie jest to naprawdę dobry pomysł, ale w praktyce nie jest tak dobry.

CodeART
źródło
Jest to również wspomniane w dokumentacji
levininja,
30
„Wyobraź sobie, że masz publiczny interfejs ILog z domyślną implementacją dziennika klasy publicznej”. - Masz zatem kiepską nazwę dla domyślnej implementacji. Co ma Logzrobić? Zaloguj się do konsoli? Aby syslog? Do nikąd? Te powinny być nazywane ConsoleLog, SyslogLogi NullLog, odpowiednio. Lognie jest dobrą nazwą dla konkretnej klasy, ponieważ nie jest to konkretna nazwa.
Sebastian Redl
7
Osobiście właśnie dlatego nienawidzę widzieć ITheClassName, czy po prostu tworzysz interfejs bez powodu, czy to tylko szum na szczycie TheClassName. Jeśli twój interfejs ma jakiś cel, powinno być możliwe nadanie mu lepszej nazwy niż ITheClassName
Richard Tingle
Nazwa klasy jest używana raz (w celu utworzenia instancji), nazwa interfejsu jest używana wszędzie. Dodanie litery do interfejsu w celu zapisania litery w nazwie klasy jest fałszywą ekonomią (znaków).
sergut
@RichardTingle Ale myślę, że powszechne użycie jest po prostu tak, że MyClassma możliwą do naśladowania odmianę, prawda? Oznacza to, że często korzystają z interfejsów do zasadniczo wydawać publiczne metody naprawdę publicznej w sposób, który pozwala na testowanie. (Nie mówienie, że to dobrze czy źle, ale zrozumienie, że motywacją do tworzenia interfejsów jest często po prostu uczynienie klas, które są zależnościami, łatwymi do naśladowania, tłumaczy mapowanie 1 MyClassna 1 IMyClass).
ruffin
8

Cóż, tu nie chodzi o implementację interfejsu czy rozszerzenie klasy. W takich przypadkach i tak wiesz, co robisz.

Jednak gdy kod strony trzeciej (inny moduł aplikacji na przykład) manipuluje danymi, kod ten nie powinien obchodzić się, jeśli prezentujesz interfejs lub klasę.

To jest cały punkt abstrakcji. Przedstawiasz temu kodowi zewnętrznemu obiekt określonego typu. Ten typ ma funkcję członka, którą możesz wywołać. Wystarczy.

Jeśli Twój typ to interfejs lub klasa to Twoja firma, a nie osoba korzystająca z Twojego kodu. Dlatego nie powinieneś przeciekać szczegółów swojego kodu do tego kodu innej firmy.

Nawiasem mówiąc, interfejsy i klasy są na końcu typami referencyjnymi. I to się liczy. Właśnie to musi podkreślić twoja konwencja nazewnictwa.

deadalnix
źródło
@Mat> Tak, to był mój błąd i ja go zredagowałem.
deadalnix
5

Większość odpowiedzi wydaje się zakładać, że językiem programowania jest Java lub C #, gdzie istnieje koncepcja (lub słowo kluczowe) dla interfejsów / klas abstrakcyjnych. W takich przypadkach, w przyzwoitym IDE, od razu widać, z jakim rodzajem klasy pierwszej zajmuje się, a pisanie public interface IMyClassjest niepotrzebnym powielaniem. To tak, jakbyś nie pisał public final FinalMyClass- wyobraź sobie umieszczenie każdego słowa kluczowego w nazwie klasy.

Jednak moim zdaniem w C ++ sytuacja jest nieco inna, ponieważ należy zagłębić się w plik nagłówka i sprawdzić, czy klasa ma jakieś wirtualne metody, aby dowiedzieć się, czy jest to klasa abstrakcyjna. W takim przypadku wyraźnie widzę argument za pisaniem class AbstractMyClass.

Tak więc moja odpowiedź brzmi: to zależy od języka programowania.

Zaktualizowany 2016: 2 lata doświadczenia w C ++ później prawdopodobnie uznałbym za niewłaściwą praktykę dodawanie nazw klas Abstract, chociaż powiedziałbym, że istnieją podstawy kodu, które są tak złożone, że może to mieć sens.

Ela782
źródło
Czy jesteś pewien, że to odpowiada na pytanie? Możesz przeczytać inne odpowiedzi i wyjaśnić niektóre z twoich punktów.
david.pfx
C ++ zdecydowanie ma interfejsy, nawet jeśli nie ma dla niego słowa kluczowego. Jak nazywacie klasę, w której każda funkcja składowa jest czysto wirtualna i nie ma zmiennych składowych?
@ david.pfx: Myślę, że dokładnie odpowiadam na pytanie „Czy nazwy interfejsów powinny zaczynać się od prefiksu„ I ”?”: Zależy to od języka programowania. Jeśli uważasz, że moje opracowanie nie jest wystarczająco jasne, cieszę się, że mogę je ulepszyć - które części nie są dla Ciebie jasne?
Ela782
2
@JohnGaughan: Pewnie, że ma interfejsy! Ale cały mój punkt widzenia jest to, że jest o słowa kluczowego. C ++ go nie ma, więc nie jest on widoczny dla IDE / użytkownika, jeśli on / ona ma do czynienia z interfejsem, czy nie. W Javie jest jednak natychmiast widoczne.
Ela782
1
Przeczytaj odpowiedź o najwyższej ocenie i porównaj swoją. Jesteś nowy w SO i jest to okazja, aby dowiedzieć się, jakie odpowiedzi przyciągają głosy. W tej chwili twoje nie. Więcej pracy, więcej wyjaśnień, więcej wartości, może po prostu. Powieś tam!
david.pfx
4

Postępuj zgodnie z idiomami używanego języka.

Każdy język ma swoje własne idiomy i wytyczne dotyczące kodowania. Na przykład w języku C # idiomatyczne jest używanie prefiksów z I. W Go nie jest.

Pete
źródło
+1 Postępuj zgodnie z idiomami używanego języka. Oczywiście autor książki jest programistą Java. .NET / C #: ILog Java: Log Ale wada: kiedy lubię mieć klasę Log, która implementuje interfejs ILog .... MyLog, LogDefault, LogBase, ...? Brzydkie, prawda? Bardziej brzydkie jest: LogImpl ....: D
hfrmobile
0

W moim kodzie (Java) mam tę konwencję w interfejsach API, które udostępniam dzwoniącym:

  • Funkcjonalność jest zapewniana przez interfejsy, a nigdy przez klasy.
  • Części niefunkcjonalne to klasy.

Przez niefunkcjonalne rozumiem takie rzeczy jak czyste struktury danych (takie jak klasy, które działają jak pomosty do serializacji XML lub ORM), wyliczenia i wyjątki, z których wszystkie nie mogą być interfejsami (cóż, możesz to zrobić dla czystych klas danych , ale jest to dużo pracy przy niewielkim zysku, ponieważ klasy te nie robią nic oprócz przechowywania danych).

Jeśli chodzi o konwencje nazewnictwa, interfejsy mają tendencję do mapowania na rzeczowniki aktorskie (np. FooBarWatcher) Lub przymiotniki (np. FooBarWatchable), A zarówno czyste klasy danych, jak i wyliczenia mapowane na nieaktywne rzeczowniki (np. FooBarStatus); IDE może poprowadzić konsumenta interfejsu API bez specjalnych konwencji nazewnictwa. Wyjątki wykonaj zwykłe konwencje Java ( FooBarException, FooBarError, FooBarFault) Oczywiście.

Często również umieszczam interfejsy w ich własnych pakietach, a nawet we własnej bibliotece, aby upewnić się, że nie mam ochoty łamać własnych zasad. (Pomaga mi to również zarządzać kompilacją, gdy czerpię kod ze źródeł zewnętrznych, takich jak dokumenty WSDL).

Donal Fellows
źródło
Nigdy też nie umieszczam Iprefiksów w interfejsach. Z Oczywiście jest to interfejs! Jest w interfejsie API (lub SPI)!
Donal Fellows