JavaRush /Blog Java /Random-PL /Przerwa kawowa #64. Jak pisać czysty kod. Dlaczego Java j...

Przerwa kawowa #64. Jak pisać czysty kod. Dlaczego Java jest lepsza niż C++ w przypadku systemów o niskim opóźnieniu

Opublikowano w grupie Random-PL

Jak pisać czysty kod

Źródło: Dev.to Pisanie czystego kodu jest jak pisanie poezji. To poezja, która powinna być zwięzła, zrozumiała i podatna na zmiany. Czysty kod oznacza skalowalną organizację. Oznacza to, że wprowadzanie zmian nie powoduje chaosu. Umiejętność pisania takiego kodu to jedna z kluczowych cech doświadczonego programisty. Po tym, jak kilka osób poleciło mi przeczytanie książki Czysty kod, w końcu zdobyłem się na odwagę, aby ją przeczytać. Okazało się, że jest to jedna z tych książek, których okładka całkowicie odzwierciedla otaczający ją szum. Zalecenia zawarte w książce są jasne, konkretne, praktyczne, a nawet przedstawione z humorem. Dziś chcę podzielić się z Wami głównymi wnioskami z tej książki.Przerwa kawowa #64.  Jak pisać czysty kod.  Dlaczego Java jest lepsza niż C++ w przypadku systemów o niskim opóźnieniu - 1

1. Kod powinien nie tylko działać, ale także być czytelny

Większość kosztów oprogramowania jest powiązana z długoterminowym wsparciem. Dlatego kod, który piszesz, musi jasno wyrażać Twoje intencje. Powinno być tak, aby nowi programiści dołączający do zespołu mogli łatwo zrozumieć, co dokładnie dzieje się w kodzie i dlaczego. Im bardziej zrozumiały kod napisze autor, tym mniej czasu zajmie innym programistom jego zrozumienie. Zmniejsza to liczbę usterek i koszty konserwacji. Jak to osiągnąć? Dobre nazewnictwo + klasy i funkcje z pojedynczą odpowiedzialnością + pisanie testów.

2. Później znaczy nigdy

Bądźmy szczerzy: czasami obiecujemy sobie, że wrócimy później i uprzątniemy kod, ale w końcu o tym zapominamy. Nie zostawiaj fragmentów bezużytecznego kodu, które nie są już potrzebne. Dezorientują innych programistów i nie mają żadnej wartości. Dlatego wprowadzając zmiany w funkcjonalności, zawsze usuwaj stary kod. Jeśli coś gdzieś pęknie, testy i tak od razu to wykażą. Jak to osiągnąć? Usuwanie kodu może być przerażające, szczególnie w dużych architekturach. Dlatego testowanie jest tutaj kluczowe. Pozwalają na pewne usunięcie kodu.

3. Funkcje powinny być małe

Pierwsza zasada pisania funkcji jest taka, że ​​powinny one być małe, do około 20 linii . Im mniejsza funkcja i im bardziej skupiona jest na jednym zadaniu, tym łatwiej znaleźć dla niej dobrą nazwę. Jeśli chodzi o argumenty funkcji, ich idealna liczba to 0. Następnie przychodzą 1, 2, ale powinieneś starać się mieć nie więcej niż 3 argumenty. Jak to osiągnąć? Funkcje należy pisać zgodnie z zasadami pojedynczej odpowiedzialności i mieć charakter otwarty/zamknięty.

4. Powielanie kodu jest złe

Powielanie jest wrogiem dobrze zorganizowanego systemu. To dodatkowa praca, dodatkowe ryzyko i dodatkowa niepotrzebna złożoność. Co z tym zrobić? Upewnij się, że Twój kod jest napisany zgodnie z zasadą DRY, izolowany i modułowy.

5. Jedyny dobry komentarz to ten, w którym znalazłeś sposób, aby go nie pisać.

„Nie ma nic bardziej przydatnego niż dobry komentarz we właściwym miejscu. Ale komentarze, nawet w najlepszym przypadku, są złem koniecznym”. Komentarze mają na celu zrekompensować naszą niezdolność do wyrażenia naszych myśli w kodzie. Oznacza to, że jest to początkowo przyznanie się do porażki. Tak, musimy ich używać, ponieważ nie zawsze możemy jasno wyrazić nasze intencje za pomocą kodu, ale nie jest to powód do świętowania. Rzecz w tym, że komentarze często kłamią. Nie zawsze i nie celowo, ale zbyt często. Im starszy jest komentarz i im dalej od kodu, który opisuje, tym większe jest prawdopodobieństwo, że będzie nieprawidłowy. Powód tego jest prosty: programiści nie są w stanie dobrze zarządzać zarówno kodem, jak i wszystkimi komentarzami. Dlatego bardzo często komentarze są oddzielane od kodu, do którego się odnoszą i z minimalną precyzją stają się adnotacjami osieroconymi. Co z tym zrobić? Należy stosować opisowe metody nazewnictwa. Kiedy czytasz nazwę zmiennej, powinieneś natychmiast zrozumieć, co to jest. Testy są również potrzebne, aby inni programiści zrozumieli, która funkcjonalność jest najważniejsza.

6. Obiekt ujawnia zachowanie, ale nie dane.

Moduł nie powinien wiedzieć o wnętrzu obiektów, którymi manipuluje. Obiekty ukrywają swoje dane i ujawniają swoje operacje. Oznacza to, że obiekt nie powinien ujawniać swojej wewnętrznej struktury za pomocą metod dostępu. Nie jest konieczne, aby wszyscy widzieli Cię nago. Co z tym zrobić? Zakres zmiennych powinien być jak najbardziej lokalny, aby nie eksponować więcej niż to konieczne.

7. Testowanie

Kod testowy jest tak samo ważny jak to, co trafia do produkcji. Dlatego musi się zmieniać i rozwijać w miarę rozwoju projektu. Dzięki testom Twój kod jest elastyczny, łatwy w utrzymaniu i nadaje się do ponownego użycia. Bez nich każda zmiana może skutkować błędami. Testy pozwalają oczyścić kod bez obawy, że coś się zepsuje. Dlatego utrzymanie czystości testów ma ogromne znaczenie. Czystość testów zapewnia ich czytelność. Testy są okazją do wyjaśnienia innym programistom prostym językiem intencji autora kodu. Dlatego testujemy tylko jedną koncepcję w każdej funkcji testowej. Dzięki temu test ma charakter opisowy, jest łatwiejszy do odczytania, a w przypadku niepowodzenia łatwiej jest znaleźć przyczynę. Jak to osiągnąć? Należy przestrzegać zasad czystych PIERWSZYCH testów . Testy powinny być:
  • Szybko. Testy muszą przebiegać szybko. Jeśli będziesz musiał zbyt długo czekać na uruchomienie testu, istnieje mniejsze prawdopodobieństwo, że będziesz go uruchamiał częściej.
  • Niezależny / izolowany (Niezależny). Testy powinny być możliwie izolowane i niezależne od siebie.
  • Powtarzalne. Testy powinny być powtarzalne w każdym środowisku — programistycznym, testowym i produkcyjnym.
  • Samoweryfikacja. Wynik testu musi być wartością logiczną. Test musi albo przejść pomyślnie, albo nie.
  • Dokładny. Powinniśmy dążyć do objęcia testami wszystkich przypadków brzegowych, wszystkich kwestii bezpieczeństwa, każdego przypadku użycia (przypadku użycia) i szczęśliwej ścieżki (najkorzystniejszy scenariusz dla kodu).

8. Obsługa błędów i wyjątków

Każdy zgłoszony wyjątek powinien zapewniać wystarczający kontekst, aby określić źródło i lokalizację błędu. Zwykle masz ślad stosu dowolnego wyjątku, ale ślad stosu nie informuje o celu operacji, która się nie powiodła. Jeśli to możliwe, unikaj przekazywania wartości null w kodzie. Jeśli masz ochotę zwrócić wartość null z metody, rozważ zamiast tego zgłoszenie wyjątku. Spraw, aby obsługa błędów była oddzielnym zadaniem, które można przeglądać niezależnie od głównej logiki. Jak to osiągnąć? Twórz informacyjne komunikaty o błędach i przekazuj je wraz z wyjątkami. Określ operację, która nie powiodła się i typ błędu.

9. Zajęcia

Klasy powinny być małe. Ale to nie linie kodu należy policzyć, ale odpowiedzialność. Nazwy klas są kluczem do opisania tego, za co są odpowiedzialne. Nasze systemy powinny składać się z wielu małych klas, a nie kilku dużych. Każda taka mała klasa musi wiązać się z jedną odpowiedzialnością. Musi istnieć tylko jeden konkretny powód istnienia każdej klasy i każda klasa musi „współpracować” z kilkoma innymi klasami, aby osiągnąć pożądane zachowanie systemu. Rzadko istnieje dobry powód do utworzenia zmiennej publicznej. Osłabienie hermetyzacji jest zawsze ostatecznością. Ponadto powinno być kilka zmiennych instancji. Dobry projekt oprogramowania pozwala na wprowadzanie zmian bez dużych inwestycji i przeróbek. Zawężenie zakresu zmiennych ułatwia to zadanie. Jak to osiągnąć? Rozdzielanie obaw to jedna z najstarszych i najważniejszych technik projektowania. Zajęcia powinny być otwarte na rozszerzenie, ale zamknięte na modyfikacje. W idealnym systemie udostępniamy nowe funkcje poprzez rozbudowę systemu, a nie wprowadzanie zmian w istniejącym kodzie.

10. Formatowanie

Każda pusta linia jest wizualną wskazówką pomagającą rozpoznać, że rozpoczął się nowy, odrębny koncept. Zmienne lokalne muszą znajdować się na górze funkcji. Zmienne instancji muszą być zadeklarowane na górze klasy. Krótkie linie są lepsze niż długie. Zwykle limit wynosi 100-120 znaków i nie należy go wydłużać. Jak to osiągnąć? Większość parametrów można przekazać do lintera w CI lub edytorze tekstu. Użyj tych narzędzi, aby Twój kod był tak czysty, jak to możliwe.

Zasady opracowywania programu

Użyj następujących technik, a Twój kod będzie zawsze czysty: Nazywanie zmiennych. Wybór odpowiednich nazw (dobre nazewnictwo) ma kluczowe znaczenie, aby kod był czytelny, a zatem łatwy w utrzymaniu. „Powinieneś wybrać imię dla zmiennej tak odpowiedzialnie, jak wybrałbyś imię swojego pierworodnego”. Wybór dobrych nazw jest często wyzwaniem dla programistów. Wymaga to dobrych umiejętności opisowych i wspólnego tła kulturowego. Czysty kod to kod, który jest czytany i ulepszany przez zupełnie różnych programistów. Nazwa zmiennej, funkcji czy klasy powinna odpowiadać na wszystkie podstawowe pytania: po co ten byt istnieje, do czego i jak się go używa. Jeśli nazwa wymaga komentarza, to znaczy, że nie oddaje w wystarczającym stopniu istoty tego, co opisuje. Dłuższe nazwy są ważniejsze niż krótsze, a każda nazwa, którą można przeszukiwać, jest lepsza niż stała. Nazwy jednoliterowe mogą być używane wyłącznie jako zmienne lokalne w krótkich metodach: długość nazwy musi odpowiadać zakresowi. Nazwy metod muszą być czasownikami lub frazami czasownikowymi; nazwa klasy nie może być czasownikiem. Zależności należy ograniczyć do minimum. Lepiej polegać na tym, na co masz wpływ, niż na tym, czego nie możesz kontrolować. W przeciwnym razie te rzeczy będą cię kontrolować. Dokładność. Każdy fragment kodu powinien znajdować się w miejscu, w którym czytelnik spodziewa się go znaleźć. Nawigacja po bazie kodu powinna być intuicyjna, a intencje programisty powinny być jasne. Czyszczenie. Nie zostawiaj bezużytecznego kodu w bazie kodu (starego i nieużywanego lub utworzonego „na wszelki wypadek”). Ogranicz powielanie prac i twórz proste abstrakcje już na wczesnym etapie. Normalizacja. Pisząc kod, powinieneś kierować się stylem i praktykami ustalonymi dla repozytorium. Samodyscyplina. W miarę rozwoju używanych technologii i pojawiania się nowych, programiści często chcą zmienić i ulepszyć coś w istniejącym kodzie. Nie poddawaj się zbyt szybko temu szumowi: dokładnie przestudiuj nowe stacki i tylko w konkretnym celu. Utrzymanie czystości bazy kodu to coś więcej niż tylko bycie uprzejmym wobec obecnych i przyszłych współpracowników. Jest to niezbędne dla długoterminowego przetrwania programu. Im czystszy kod, tym szczęśliwsi programiści, tym lepszy produkt i tym dłużej będzie trwał.

Dlaczego Java jest lepsza niż C++ w przypadku systemów o niskim opóźnieniu

Źródło: StackOverflow Jako programiści wszyscy wiemy, że istnieją dwa sposoby robienia rzeczy: ręcznie, powoli i irytująco lub automatycznie, trudno i szybko. Do napisania tego artykułu przydałaby mi się sztuczna inteligencja. Mogłoby mi to zaoszczędzić mnóstwo czasu – sztuczna inteligencja może generować tysiące artykułów na sekundę, ale mój redaktor prawdopodobnie nie byłby szczęśliwy, gdyby dowiedział się, że wygenerowanie pierwszego artykułu zajmie dwa lata. Przerwa kawowa #64.  Jak pisać czysty kod.  Dlaczego Java jest lepsza niż C++ w przypadku systemów o niskim opóźnieniu - 2Podobna sytuacja ma miejsce podczas tworzenia systemów oprogramowania o niskim opóźnieniu. Powszechnie uważa się, że szaleństwem byłoby używanie czegokolwiek innego niż C++, ponieważ wszystko inne ma zbyt duże opóźnienia. Jestem jednak tutaj, aby przekonać Cię do przeciwnego, sprzecznego z intuicją, niemal heretyckiego poglądu: jeśli chodzi o osiągnięcie małych opóźnień w systemach oprogramowania, Java jest lepsza. W tym artykule chcę wziąć konkretny przykład oprogramowania, które ceni sobie małe opóźnienia: systemy handlowe. Jednakże przedstawione tutaj argumenty można zastosować w niemal każdej sytuacji, w której wymagane lub pożądane jest małe opóźnienie. Po prostu łatwiej jest dyskutować w odniesieniu do obszaru rozwoju, w którym mam doświadczenie. A prawda jest taka, że ​​opóźnienie jest trudne do zmierzenia. Wszystko sprowadza się do tego, co rozumiesz przez małe opóźnienia. Rozwiążmy to teraz.

Nabyta Mądrość

Ponieważ C++ jest znacznie bliższy sprzętowi, większość programistów powie Ci, że kodowanie w tym języku zapewnia przewagę szybkości. W sytuacjach małych opóźnień, takich jak szybki handel, gdzie milisekundy mogą zadecydować o różnicy między wykonalnym oprogramowaniem a stratą miejsca na dysku, C++ jest uważany za złoty standard. Przynajmniej tak było kiedyś. Ale rzeczywistość jest taka, że ​​wiele dużych banków i brokerów korzysta obecnie z systemów napisanych w Javie. Mam na myśli natywnie napisany w Javie, a nie napisany w Javie, a następnie zinterpretowany w C++ w celu zmniejszenia opóźnień. Systemy te stają się standardem nawet dla banków inwestycyjnych Tier 1, mimo że są (podobno) wolniejsze. Więc co się dzieje? Tak, C++ może mieć „małe opóźnienia”, jeśli chodzi o wykonywanie kodu, ale zdecydowanie nie jest to małe opóźnienie, jeśli chodzi o wdrażanie nowych funkcji lub nawet znajdowanie programistów, którzy mogą je napisać.

(Prawdziwe) różnice między Javą i C++

Kwestia czasu rozwoju to dopiero początek, jeśli chodzi o różnice między Javą i C++ w rzeczywistych systemach. Aby zrozumieć prawdziwą wartość każdego języka w tym kontekście, zagłębimy się nieco głębiej. Po pierwsze, ważne jest, aby pamiętać prawdziwy powód, dla którego C++ jest szybszy niż Java w większości sytuacji: wskaźnik C++ to adres zmiennej w pamięci. Oznacza to, że oprogramowanie może uzyskać bezpośredni dostęp do poszczególnych zmiennych i nie musi przeszukiwać tabel wymagających dużej mocy obliczeniowej, aby je wyszukać. Lub przynajmniej można rozwiązać, określając, gdzie się one znajdują, ponieważ w C++ często trzeba jawnie zarządzać czasem życia i własnością obiektów. W rezultacie, jeśli nie jesteś naprawdę dobry w pisaniu kodu (umiejętność, której opanowanie może zająć dziesięciolecia), C++ będzie wymagał godzin (lub tygodni) debugowania. I jak powie każdy, kto próbował debugować silnik Monte Carlo lub narzędzie testowe PDE, próba debugowania dostępu do pamięci na podstawowym poziomie może być bardzo czasochłonna. Tylko jeden wadliwy wskaźnik może z łatwością unieruchomić cały system, dlatego wypuszczenie nowej wersji napisanej w C++ może być naprawdę przerażające. Oczywiście to nie wszystko. Osoby, które lubią programować w C++, zwrócą uwagę, że moduł zbierający elementy bezużyteczne w Javie cierpi na nieliniowe skoki opóźnień. Jest to szczególnie prawdziwe podczas pracy ze starszymi systemami, więc wysyłanie aktualizacji kodu Java bez psucia systemów klienckich może spowodować, że będą one tak powolne, że staną się bezużyteczne. W odpowiedzi chciałbym podkreślić, że w ciągu ostatniej dekady wykonano wiele pracy, aby zmniejszyć opóźnienia powodowane przez Java GC. Zakłócacz LMAXto na przykład platforma transakcyjna o niskim opóźnieniu napisana w języku Java, zbudowana również jako framework, który ma „mechaniczną interakcję” ze sprzętem, na którym działa i nie wymaga blokowania. Problemy można jeszcze bardziej złagodzić, jeśli zbudujesz system wykorzystujący proces ciągłej integracji i dostarczania (CI/CD), ponieważ CI/CD umożliwia automatyczne wdrażanie przetestowanych zmian w kodzie. Dzieje się tak dlatego, że CI/CD zapewnia iteracyjne podejście do zmniejszania opóźnień w usuwaniu elementów bezużytecznych, w ramach którego Java może stopniowo udoskonalać się i dostosowywać do określonych środowisk sprzętowych bez konieczności angażowania dużych zasobów w proces przygotowywania kodu dla różnych specyfikacji sprzętu przed jego wysyłką. Ponieważ obsługa Java w IDE jest znacznie szersza niż w C++, większość frameworków (Eclipse, IntelliJ IDEA) umożliwia refaktoryzację Java. Oznacza to, że IDE mogą optymalizować kod pod kątem wydajności z niskimi opóźnieniami, chociaż ta możliwość jest nadal ograniczona podczas pracy z C++. Nawet jeśli kod Java nie dorównuje szybkością C++, większości programistów nadal łatwiej jest osiągnąć akceptowalną wydajność w Javie niż w C++.

Co rozumiemy przez „szybciej”?

W rzeczywistości istnieje dobry powód, aby wątpić, czy C++ jest naprawdę „szybszy” lub nawet ma „mniejsze opóźnienia” niż Java. Zdaję sobie sprawę, że wchodzę w dość mętne wody i że wielu programistów zacznie kwestionować mój zdrowy rozsądek. Ale wysłuchaj mnie. Wyobraźmy sobie taką sytuację: masz dwóch programistów – jeden pisze w C++, drugi w Javie i prosisz ich o napisanie od podstaw szybkiej platformy transakcyjnej. W rezultacie system napisany w Javie będzie potrzebował więcej czasu na realizację transakcji handlowych niż system napisany w C++. Jednak Java ma znacznie mniej przypadków niezdefiniowanego zachowania niż C++. Weźmy tylko jeden przykład: indeksowanie poza tablicą jest błędem zarówno w Javie, jak i C++. Jeśli przypadkowo zrobisz to w C++, może wystąpić błąd segfaultowy lub (częściej) po prostu otrzymasz losową liczbę. W Javie wyjście poza zakres zawsze powoduje błąd ArrayIndexOutOfBoundsException . Oznacza to, że debugowanie w Javie jest znacznie łatwiejsze, ponieważ błędy są zazwyczaj natychmiast identyfikowane, a lokalizacja błędu jest łatwiejsza do wyśledzenia. Ponadto, przynajmniej z mojego doświadczenia, Java lepiej rozpoznaje, które fragmenty kodu nie muszą zostać uruchomione, a które są krytyczne dla działania oprogramowania. Można oczywiście spędzać dni na poprawianiu kodu C++ tak, aby nie zawierał żadnego zbędnego kodu, ale w prawdziwym świecie każdy fragment oprogramowania zawiera pewne wzdęcia, a Java lepiej rozpoznaje go automatycznie. Oznacza to, że w prawdziwym świecie Java jest często szybsza niż C++, nawet według standardowych wskaźników opóźnień. Nawet jeśli tak nie jest, różnica w opóźnieniu między językami jest często przytłaczana przez inne czynniki, które nie są na tyle duże, aby mieć znaczenie nawet w przypadku szybkiego handlu.

Korzyści z języka Java dla systemów o niskim opóźnieniu

Wszystkie te czynniki, moim zdaniem, stanowią całkiem przekonujący argument za używaniem języka Java do pisania szybkich platform transakcyjnych (i ogólnie systemów o niskim opóźnieniu, o czym za chwilę). Aby jednak nieco przekonać entuzjastów C++, spójrzmy na kilka dodatkowych powodów, dla których warto używać Java:
  • Po pierwsze, wszelkie nadmierne opóźnienia, jakie Java wprowadza do oprogramowania, będą prawdopodobnie znacznie mniejsze niż inne czynniki wpływające na opóźnienia, takie jak problemy z Internetem. Oznacza to, że w większości sytuacji handlowych każdy (dobrze napisany) kod Java może z łatwością działać równie dobrze jak C++.

  • Krótszy czas rozwoju Javy oznacza również, że w prawdziwym świecie oprogramowanie napisane w Javie może dostosowywać się do zmian sprzętowych (lub nawet nowych strategii handlowych) szybciej niż C++.

  • Jeśli zagłębisz się w ten temat, zobaczysz, że nawet optymalizacja oprogramowania Java może być szybsza (biorąc pod uwagę całe oprogramowanie) niż podobne zadanie w C++.

Innymi słowy, możesz bardzo dobrze napisać kod Java, aby zmniejszyć opóźnienia. Wystarczy napisać to w C++, pamiętając o zarządzaniu pamięcią na każdym etapie programowania. Zaletą niepisania w C++ jest to, że debugowanie, zwinne programowanie i dostosowywanie się do wielu środowisk jest łatwiejsze i szybsze w Javie.

wnioski

Jeśli nie tworzysz systemów handlowych o niskim opóźnieniu, prawdopodobnie zastanawiasz się, czy którekolwiek z powyższych dotyczy Ciebie. Odpowiedź, z nielicznymi wyjątkami, brzmi: tak. Debata na temat tego, jak osiągnąć niskie opóźnienia, nie jest ani nowa, ani wyjątkowa w świecie finansów. Z tego powodu można z niego wyciągnąć cenne wnioski na temat innych sytuacji. W szczególności powyższy argument, że Java jest „lepsza”, ponieważ jest bardziej elastyczna, bardziej odporna na błędy i ostatecznie szybsza w tworzeniu i utrzymaniu, można zastosować w wielu obszarach tworzenia oprogramowania. Powody, dla których (osobiście) wolę pisać systemy o niskim opóźnieniu w Javie, są tymi samymi powodami, które zapewniły temu językowi tak duży sukces przez ostatnie 25 lat. Java jest łatwa do napisania, kompilowania, debugowania i nauki. Oznacza to, że możesz spędzić mniej czasu na pisaniu kodu, a więcej na jego optymalizacji. W praktyce prowadzi to do bardziej niezawodnych i szybszych systemów handlowych. I to wszystko, co ma znaczenie w przypadku szybkiego handlu.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION