15 съвета за писане на самодокументиращ се код (използвайки JavaScript като пример)
Преглед на техниката
- структурен, при който структурата на кода или директориите се използва за изясняване на предназначението му;
- свързани с именуване, като именуване на функции и променливи;
- свързани със синтаксиса, в който за тази цел се използват (или не се използват) отделни особености на езика.
Някои от тези техники са прости на хартия, но трудни за използване, така че ще покажа практически примери, докато описвам всяка от тях.
Структурни
Първо, нека да разгледаме структурните техники, те се изпълняват чрез специално поставяне на код, за да се увеличи неговата яснота.
Преместване на код във функция
Това е същото като рефакторингът за „извличане на функции“ – означава, че вземаме съществуващия код и го преместваме в нова функция: „избираме“ кода в нова функция.
Например, опитайте се да познаете какво прави следният ред код:
Единствената промяна е, че изчисленията са преместени в отделна функция. Името на функцията описва какво прави и този код не се нуждае от допълнително пояснение. Като допълнителен бонус получаваме полезна помощна функция, която можете да използвате навсякъде, което означава, че този метод също така избягва дублирането.
Замяна на условен израз с функция
За какво се използва условието в този пример?
Отново преместихме кода във функция и кодът веднага стана много по-очевиден.
Замяна на променлив израз
Замяната на нещо с променлива е като поставяне на код във функция, но вместо функция ние просто използваме променлива.
Разгледайте отново предишния пример с условието:
Вместо да маркираме функция, можем да я направим по-ясна, като добавим променлива:
Това може да е най-добротоопция, отколкото подчертаване на функция - например, когато логиката, която искате да прецизирате, е много специфична за определен алгоритъм, използван само на едно място.
Най-често този метод се използва с математически изрази:
Можем да направим този пример по-ясен, като разделим изчисленията:
Зле съм с математиката, така че си представете някакъв по-полезен алгоритъм вместо този пример. Във всеки случай можете да преместите сложни изрази в променливи, за да добавите смисъл към труден за разбиране код.
Интерфейси на класове и модули
Интерфейси, т.е. публични методи и свойства на клас или модул, могат да действат като документация за неговото използване.
Този клас може да съдържа и друг код, разбира се. Умишлено поддържам примера прост, за да илюстрирам как публичен интерфейс може да служи като документация.
Можете ли да кажете как се използва този клас? Може би можете, прекарвайки време, но не е веднага очевидно.
И двете функции имат смислени имена, които ясно показват какво правят. Но това не дава точно разбиране за това как трябва да ги използвате. Най-вероятно ще трябва да проучите повече код или документация, за да го разберете.
Ами ако променим този код по следния начин:
Сега приложението на този код е по-ясно, нали? Обърнете внимание, че сме променили само публичния интерфейс, вътрешната реализация е същата като в примера, използвайки свойството this.state.
Групиране на кодове
Групирането на различни части от код може да работи като форма на документация.
Например, човек винаги трябва да се стреми да декларира променливи възможно най-близо до мястото, където се използват, и да групира използваните променливизаедно.
Това може да се използва за посочване на връзките между различните части на кода, така че когато правите промени в този код, ще бъде по-лесно да видите кои части от кода също трябва да бъдат променени.
Можете веднага да видите колко често се използва foo. Сравнете със следното:
Чрез групиране на всички употреби на foo можем веднага да видим кои части от кода зависят от тази променлива.
Използвайте чисти функции
Чистите функции са много по-лесни за разбиране от зависимите от състоянието функции.
Какво е чиста функция? Когато такава функция се извика с едни и същи параметри, тя винаги произвежда един и същ резултат, това обикновено се нарича "чиста" функция. Това означава, че функцията няма странични ефекти или зависимост от състояние (време, свойства на обекта, Ajax).
Този тип функция е по-лесна за разбиране, тъй като всички стойности, които влияят на резултата, се предават изрично като аргументи. Не е нужно да търсите дълго време откъде идва нещо или какво е повлияло на резултата, тъй като всичко е на видно място.
Друга причина подобни функции да правят кода самодокументиращ се е, че можете да се доверите на техния резултат. Без значение какво, функцията винаги ще връща резултат, базиран единствено на параметрите, които са й предадени. Те също не се влияят от външни фактори, така че можете да сте сигурни, че няма да предизвикат неочакван страничен ефект.
За по-добър преглед на концепцията за чистите функции препоръчвам да прочетете статията Функционално програмиране: чисти функции.
Структура на файлове и директории
Когато именувате файлове и директории, трябва да следвате една и съща система за именуване в целия проект. Ако в проекта няма изрична система за именуване, следвайте стандарта за езика, който използвате.
Например, ако добавяте нов UI код, потърсете подобна функционалност във вашия проект. Ако такъв код е поставен в src/ui/, поставете и нов код там.
Това улеснява намирането на кода и показва неговата цел въз основа на това, което вече знаете за други части от код в проекта. Поставянето на целия UI код на едно място прави ясно какви задачи изпълнява.
именуване
Има популярен цитат за двете най-трудни неща в програмирането:
Има само две наистина трудни неща: обезсилване на кеша и именуване на обект – Фил Карлтън, 21 окт. '11 в 10:03
Така че нека да видим как можем да използваме именуване, за да направим кода самодокументиращ се.
Преименуване на функция
Наименуването на функции обикновено не е твърде сложно, но има някои правила, които можете да следвате:
- Избягвайте да използвате неясни думи като "обработка" или "управление": handleLinks() , manageObjects() .
- Използвайте активни глаголи: cutGrass() , sendFile() – очевидно е какво правят тези функции.
- Обозначете върнатата стойност: getMagicBullet(), readFile(). Това не трябва да се прави през цялото време, но помага там, където има смисъл.
- В строго типизираните езици можете да използвате декларация на тип, която също помага да се обозначат връщаните стойности.
Преименуване на променлива
Има две добри правила за променливите:
- Посочете единици: Ако имате числови параметри, можете да ги включите в заглавието като очаквани единици. Например widthPx вместо width показва, че се използва стойност на пиксел.
- Не използвайте съкращения: имена като a или b са безполезни за нищо,с изключение на броячи в цикли.
Следвайте приетата система за именуване
Опитайте се да следвате същата система за именуване във вашия код. Например, ако имате обект от определен тип, използвайте подходящото име:
Не използвайте спонтанно други термини:
Ако следвате една и съща система за именуване в цялата кодова база, по-късно можете да правите предположения за значението на нещата въз основа на тяхното значение другаде.
Използвайте смислени съобщения за грешки
Undefined не е обект!
Какво прави едно съобщение за грешка значимо?
- трябва да описва възникналия проблем;
- ако е възможно, трябва да включва стойностите на променливите или други данни, които са причинили грешката;
- Най-важното е, че съобщението за грешка трябва да ни помогне да идентифицираме какво се е объркало - действайки като документация за това как функцията работи правилно.
Свързаните със синтаксиса техники за писане на самодокументиращ се код могат да бъдат по-специфични за всеки отделен език. Например Ruby и Perl ви позволяват да използвате всякакви странни синтактични трикове, които обикновено трябва да се избягват.
Не използвайте синтактични трикове
Не използвайте странни трикове. Ето един добър начин да объркате хората:
Това е еквивалентно на следния, по-подходящ изглеждащ код:
Винаги предпочитайте последния вариант. Синтаксичните трикове не носят никаква полза.
Използвайте именувани константи, избягвайте магически числа
Ако имате специални стойности в кода си, като числа или низове, използвайте константи вместо това. Дори сега да изглежда очевидно, след месец или два никой няма да си спомня защо е използваноконкретно този номер:
Ако не използвате ES6, можете да използвате var за същата цел, всичко ще работи по същия начин.
Избягвайте булеви флагове
Булевите флагове могат да направят кода труден за разбиране. Помислете за пример:
Какво означава вярно? Това е напълно неразбираемо, докато не се разровите в източниците на setData().
Вместо това можете да добавите друга функция или да преименувате съществуваща:
Сега можете веднага да разберете какво се случва.
Възползвайте се от езиковите функции
Можем дори да използваме отделни характеристики на избрания език, за да съобщим по-добре целта на конкретен код.
Този код събира списъка с идентификатори в нов масив. Въпреки това, за да разберем за това, трябва да прочетем целия код на цикъла. Сравнете това с метода map():
В този случай веднага знаем, че се създава нов масив с нещо, тъй като това се получава след операцията map(). Това може да бъде изгодно, особено ако е включена по-сложна логика. Ето списък с функции за повторение на сайта на MDN.
Често декларирате променливи, чиито стойности не възнамерявате да променяте. Често срещан пример за това е свързването на модули с CommonJS:
Можем да направим тази неизменност по-очевидна:
Това има допълнителен бонус, че ако някой случайно се опита да промени това, ще получим грешка.
Антишаблони
С всички тези функции можете да направите много добри неща. Има обаче неща, за които трябва да се погрижите.
Екстракция за няколко кратки функции
Някои хора препоръчват използването на много малки функции, можете да постигнете това, като извлечете всичко, което можете. Това обаче може да навреди на лесния за разбиране код.
Представете си, че дебъгвате някакъв код. Поглеждате функцията a() и виждате, че тя използва функция b(), която от своя страна използва функция c(). И така нататък.
Докато кратките функции са лесни за разбиране, ако използвате функцията само на едно място, по-добре е да използвате променлива, за да замените израза.
Не насилвайте
Обикновено няма абсолютно правилен начин за писане на самодокументиращ се код, така че ако смятате, че дадена техника не е добра идея, не я използвайте.