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?
źródło
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.Odpowiedzi:
Jeśli przestaniesz o tym myśleć, zobaczysz, że interfejs tak naprawdę nie różni się znacznie semantycznie od klasy abstrakcyjnej:
W rzeczywistości najważniejsze różnice między klasami i interfejsami to:
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 #,
I
ponieważ 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.źródło
I
interfejsu może dać dobrą nazwę dla klasy przechowującej metody statyczne powiązane z interfejsem (np.Enumerable<T>.Empty
lubComparer<T>.Default
).class Foo
już się rozszerzaclass Bar
, nie jest od razu oczywiste, czyclass Bar
jest rozszerzany czy implementowany. Musisz teraz zajrzeć doclass Bar
nagłówka, aby zdecydować, czy można zmodyfikowaćclass Foo
rozszerzenieclass 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.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!
źródło
Książka jest pełna dobrych rzeczy, ale nadal dodawałbym „I” do nazwy interfejsu.
Wyobraź sobie, że masz
public interface ILog
domyślną implementacjępublic class Log
.Teraz, jeśli zdecydujesz się na to
public interface Log
, nagle musisz zmienić klasę Log napublic class LogDefault
lub 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.
źródło
Log
zrobić? Zaloguj się do konsoli? Aby syslog? Do nikąd? Te powinny być nazywaneConsoleLog
,SyslogLog
iNullLog
, odpowiednio.Log
nie jest dobrą nazwą dla konkretnej klasy, ponieważ nie jest to konkretna nazwa.MyClass
ma 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 1MyClass
na 1IMyClass
).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.
źródło
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 IMyClass
jest 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.źródło
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.
źródło
W moim kodzie (Java) mam tę konwencję w interfejsach API, które udostępniam dzwoniącym:
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).
źródło
I
prefiksów w interfejsach. Z Oczywiście jest to interfejs! Jest w interfejsie API (lub SPI)!