Шість рад по написанню більш зрозумілого програмного коду, Різне, Програмування, статті

Для розробника програм найціннішим ресурсом є час. Наведені нижче шість порад допоможуть вам створювати програми, які буде легко підтримувати в майбутньому. Це заощадить вам масу часу і дозволить уникнути розчарувань: одна хвилина, витрачена на написання коментарів, здатна позбавити вас від тривалих мук.


Я пройшов важкий шлях, перш ніж навчився писати ясний програмний код, що допускає подальше обслуговування. Протягом останніх дванадцяти років я заробляв на життя, створюючи комп’ютерні ігри та продаючи їх через Інтернет у відповідності з маркетинговою моделлю під назвою shareware. Це означає, що я сідаю перед порожнім екраном і приступаю до написання програмного коду – і тільки написавши кілька десятків тисяч рядків коду, я отримую щось, що можна продати.


Іншими словами, якщо я створюю безлад, то роблю це виключно в своєму власному гнізді. Коли в 3:00 ночі я шукаю помилку в заплутаній як страшний сон “макаронної” програмі і кажу собі: “Про господи, яке дебільне породження близькоспорідненого схрещування написало цей кошмар? », відповідь на це питання може бути тільки один:« Це я сам ».


Освоєння хороших, упорядкованих методик програмування стало для мене справжнім благословенням. Деякі з таких методик описуються в даній статті. Багато вмілі, досвідчені і внутрішньо дисципліновані програмісти знають багато з подібних методик назубок. Все, що такі люди зможуть отримати з даної статті – це можливість насолодитися моїм чудовим літературним стилем і згадати, наскільки жахлива було їхнє життя до того, як вони долучилися до концепції «чистого коду».


Однак існують і багато інших людей, що прийшли, як я, до програмування несподіваними або незвичайними шляхами, яким ніхто і ніколи не розповідав подібних речей. Багато програмістів вважають мої поради тривіальними, однак для когось вони виявляться безцінними скарбами, про які вони до цього не мали уявлення. Отже, якщо ви не хочете писати плутані програми – ця стаття для вас.


Приклад


В якості ілюстрації в цій статті я буду розглядати програму, яка була гіпотетичну комп’ютерну гру під назвою Kill Bad Aliens (Убий поганого інопланетянина). Учасник цієї гри повинен керувати космічним кораблем. Цей корабель переміщається по горизонталі в нижній частині екрану і стріляє снарядами по вертикалі. Управління цим космічним кораблем буде здійснюватися, скажімо, за допомогою клавіатури.


Рис. 1. Наша гіпотетична гра
Наша гіпотетична гра

Гра розбита на тимчасові інтервали – так звані «хвилі». Протягом кожної хвилі у верхній частині екрана один за одним з’являються інопланетяни. Вони літають по екрану і кидають бомби. Інопланетяни з’являються на екрані протягом фіксованого проміжку часу. Хвиля закінчується після того, як гравець знищив певну кількість інопланетян.


За кожного вбитого інопланетянина гравцеві нараховується певна кількість балів. Після закінчення кожної хвилі гравець отримує певну кількість бонусних очок – залежно від того, наскільки швидко він зміг завершити дану хвилю.


При ураженні космічного корабля бомбою він вибухає, після чого на екрані з’являється інший корабель. Якщо космічний корабель вибухає три рази, гра завершується. Гравець, що набрав велику кількість балів, вноситься в підсумковий рейтинг. При малій кількості набраних балів цього не відбувається.


Отже, ви сідаєте за робочий стіл і починаєте писати програмний код гри Kill Bad Aliens на мові C + +. Спочатку ви визначаєте об’єкти, які будуть представляти космічний корабель, снаряди гравця, інопланетян та їх бомби. Потім ви пишете код для графічного представлення на екрані всіх зазначених об’єктів. Після цього ви пишете код для переміщення об’єктів по екрану в залежності від часу. І, нарешті, ви пишете ігрову логіку, штучний інтелект інопланетян, програмний код для введення з клавіатури команд гравця і т.д.


Виникає неминуче питання – як зробити все перераховане таким чином, щоб програмний код готової гри був зрозумілим, не створював складностей при подальшому супроводі і взагалі не був нерозбірливу плутанину?


Порада 1. Будьте розсудливі – пишіть коментарі.


Коментуйте свій програмний код. Обов’язково. Наприклад, ви написали процедуру і не супроводили її коментарями. Коли ви через кілька місяців повернетеся до неї для доопрацювання (а вам напевно доведеться це зробити), відсутність коментарів призведе до додаткових втрат робочого часу. Нагадую, що час є найціннішим ресурсом. Втрачений час неможливо компенсувати.


Слід, однак, відзначити, що написання коментарів – це теж мистецтво. Для досягнення майстерності в цьому виді діяльності необхідна практика. Коментарі бувають хороші і погані.


Не треба писати занадто довгих коментарів. Припустимо, ви супроводжували якусь функцію коментарями, які в майбутньому на десять хвилин скоротять час на розуміння вашого коду. Однак припустимо також, що ваші коментарі настільки багатослівні, що на їх написання пішло ять хвилин, і ще п’ять хвилин буде потрібно на їх читання. Таким чином, підсумкова економія часу дорівнює нулю. Не найкращий варіант.


З іншого боку, не слід писати занадто коротких коментарів. Якщо ваш програмний код довжиною в сторінку або більше нічим не переривається, то я щиро сподіваюся, що він має кришталевої прозорістю, оскільки в іншому випадку такий код неминуче приведе вас до втрати часу в майбутньому.


І, нарешті, не слід писати дурних коментарів. Коли людина вперше приступає до написання коментарів, він часто викаблучується і пише щось на кшталт:



Що тут можна сказати … Якщо який-небудь фрагмент програми настільки очевидний, він не потребує коментарів. З іншого боку, якщо ваша програма настільки заплутана, що ви повинні коментувати кожну її рядок, можливо, краще спочатку спростити її яким-небудь іншим способом. Коментарі не тільки економлять час, вони самі потребують часу. Коментарі потребують часу на прочитання, крім того, вони збільшують фактичні розміри програми на екрані монітора, в результаті чого перед вашими очима може одночасно перебувати менший обсяг чинного програмного коду.


І вже якщо ми підійшли до цього, ще одна рекомендація – ніколи не робіть такого:



Ну і що? Ми закінчили? Дякую, що повідомили мені про це. Ці квадратні дужки і фактично порожній простір між ними не несуть ніякої корисної для мене інформації. Крім того, перед оператором повернення немає необхідності вставляти коментарі виду «Тепер ми повертаємо значення».


Отож, як коментувати свою програму, якщо ви пишете програмний код за відсутності вказівок начальника або корпоративних правил? Що стосується мене, то перше, що я роблю при написанні програмного коду, який мені доведеться підтримувати самому – пишу введення. Коли я повертаюся до створеної раніше процедурою, я вже давно забув, про що думав у момент її написання, тому мені хочеться зрозуміти, що ця процедура робить. Після того як я зрозумію, що програма робить, стає значно простіше зрозуміти реальний програмний код. У загальному випадку необхідні наступні коментарі:



  1. Кілька пропозицій на початку процедури / функції, що пояснюють, що вона робить.
  2. Опис значень, переданих в цю процедуру / функцію.
  3. У випадку функції – опис сенсу повертаються параметрів.
  4. Усередині процедури / функції – коментарі, що розбивають програмний код на короткі підзадачі.
  5. Для особливо складних фрагментів коду – коротке пояснення того, що відбувається.

Отже, все, що нам необхідно – це опис на початку і кілька «покажчиків» всередині, що пояснюють обраний шлях. Все це робиться дуже швидко і в тривалій перспективі заощаджує масу часу.


Нижче наведено приклад з нашої гіпотетичної гри Kill Bad Aliens. Розглянемо об’єкт, який представляє снаряди, якими стріляє гравець. Вам доведеться часто викликати функцію, яка буде переміщати цей об’єкт вгору і визначати, куди він потрапив. Я б написав цю процедуру приблизно так:



Якщо програмний код досить акуратний, то подібних коментарів буде цілком достатньо. Однак навіть такі коментарі збережуть масу часу, якщо мені доведеться повертатися до цієї функції для виправлення небудь дурної помилки.

Порада 2. Використовуйте оператор # define частіше. ЯК МОЖНА ЧАСТІШЕ.


Припустимо, у нашій гіпотетичній грі ми хочемо, щоб при кожному попаданні в інопланетянина гравцеві нараховувалося десять балів. Це завдання можна вирішити двома способами. Поганий спосіб:



Хороший спосіб: В деякому глобальному файлі напишіть наступний рядок:


Тепер, якщо захочемо привласнити гравцеві кілька призових балів, досить написати наступне:


Більшість програмістів так чи інакше знають, що треба поступати саме так. Однак для послідовної реалізації цієї концепції необхідна внутрішня дисципліна. Майже кожен раз, коли ви вводите числову константу, треба ретельно обміркувати – не задати її в деякій «центральному пункті». Припустимо, наприклад, що ви хочете мати ігрову область з розмірами 800 х 600 пікселів. Настійно рекомендую задавати розміри цієї області наступним чином:


Якщо згодом ви вирішите змінити розміри ігрового вікна (а це досить ймовірно), то можливість централізованого зміни цих значень заощадить вам час двічі. По-перше, вам не доведеться переглядати весь свій код в пошуках всіх місць, де ви вказали ширину екрану, рівну 800 пікселів. («800 пікселів! І про що я тільки думав?”) По-друге, вам не доведеться виправляти неминучі помилки, пов’язані з посиланнями, які ви неминуче пропустите.


При роботі над грою Kill Bad Aliens мені потрібно вирішити, скільки інопланетян необхідно вбити для завершення хвилі, скільки інопланетян може знаходитися на екрані одночасно і як швидко інопланетяни з’являються на екрані. Наприклад, якщо я захочу, щоб кожна хвиля мала однакове число інопланетян, що з’являються з однаковою частотою, я, швидше за все, напишу щось подібне:



Цілком зрозумілий код. Потім, якщо я прийду до висновку про те, що хвилі занадто короткі, або що проміжки між появами інопланетян занадто малі, я зможу змінити ці значення і миттєво перенастроювати всю гру.


Одне з істотних переваг подібного механізму для завдання параметрів ігри – така можливість швидкого внесення змін доставляє щире задоволення і дозволяє відчувати себе всемогутнім. Наприклад, ви змінили вищенаведений текст наступним чином:



Тепер достатньо однієї перекомпіляції – і ваша гра стане значно веселіше і навіть безумніше.


Рис. 2. Гра Kill Bad Aliens до зміни констант
Гра Kill Bad Aliens до зміни констант

Рис. 3. Гра Kill Bad Aliens після збільшення всіх констант (грати, може бути, важкувато, але подивитися цікаво)
Гра Kill Bad Aliens після збільшення всіх констант (грати, може бути, важкувато, але подивитися цікаво)

Між іншим, ви звернули увагу, що для зазначених вище значень я не писав ніяких-яких коментарів. Це пояснюється тим, що їхній зміст очевидний з імен змінних. Це природним чином призводить нас до наступного важливого раді.


Порада 3. Не давайте змінним імена, здатні ввести в оману.


Кінцева мета проста: написати програмний код таким чином, щоб стороння людина, що не має уявлення про те, що цей код робить, міг зрозуміти його як можна швидше.


Один з основних способів досягнення цієї мети полягає в тому, щоб давати змінним, процедурам і т.д. хороші, т.зв. «Говорять» імена. Якщо згаданий вище гіпотетичний читач вашого коду, подивившись на ім’я змінної, подумає: «Ага, я розумію, що це таке», це заощадить йому п’ять хвилин – йому не доведеться переглядати вашу програму на предмет пояснень, що, врешті-решт, на думку автора має означати ім’я incremeter_side_blerfm.


При цьому необхідно дотримуватися золоту середину. Давайте своїм об’єктам імена, досить довгі і наочні для розуміння їхнього змісту, однак не настільки довгі і громіздкі, щоб це ускладнювало читання програмного коду.


Наприклад, в реальних умовах я, швидше за все не давав би константам такі довгі імена, які я застосував у попередньому розділі. Я зробив це тільки для того, щоб читач зміг повністю усвідомити їх сенс без якого-небудь контексту. В контексті самої програми замість тексту:



я, швидше за все, написав би наступне:


Будь-яке непорозуміння, обумовлене більш коротким ім’ям, буде усунуто дуже швидко, а «читаність» коду покращиться.


Тепер розглянемо фрагмент коду, який буде використовуватися дуже часто для переміщення всіх інопланетян по екрану. Я майже напевно написав би це так:



Зверніть увагу, що масив для всіх інопланетян так і називається – aliens. ТА дуже добре. Дане ім’я передає саме те, що я хотів сказати, однак при цьому воно досить короткий, щоб я міг ввести його з клавіатури тисячі разів і не зійти при цьому з розуму. Цілком ймовірно, ви будете використовувати цей масив ДУЖЕ ЧАСТО. Якщо ви назвете цей масив all_aliens_currently_on_screen, Ваш програмний код стане на десять миль довше і настільки ж незрозуміліше.


Крім того, параметру циклу я без будь-яких додаткових коментарів дав просте ім’я i. Якщо ви тільки почали освоювати стратегію описового іменування змінних, у вас може виникнути спокуса дати цієї змінної «говорить» ім’я counter (лічильник) або щось подібне до цього. Це зовсім не обов’язково. Мета іменування змінної полягає в тому, щоб негайно викликати в читача реакцію: «Ага, я знаю, що це означає». Якщо я дам цієї змінної ім’я i, j і т.д., будь-який читач відразу зрозуміє, що це параметр циклу. Яких-небудь додаткових роз’яснень не потрібно.


Безсумнівно, до іменуванню змінних можна ставитися набагато серйозніше. Наприклад, існує т.зв. угорська нотація. Ця концепція має безліч варіантів, однак її основна ідея полягає в тому, що кожне ім’я змінної має починатися з префікса, що вказує на тип цієї змінної. (Наприклад, всі змінні типу unsigned long variable повинні починатися з префікса ul і т.д.). На мій погляд, це вже перебір, однак вам треба знати про існування і такого варіанту. Можна витратити досить багато часу на те, щоб зробити речі зрозуміліше, однак рішення цього завдання також вимагає певних зусиль.


Порада 4. Перевіряйте свою програму на наявність помилок. Ви ж робите помилки. Так-так, саме ви.


Якщо ваша програма досить велика, в ній напевно буде безліч функцій і процедур. Як би це не здавалося клопітно, кожну функцію / процедуру необхідно перевіряти на наявність помилок.


При створенні будь-якої процедури / функції завжди потрібно задаватися питанням: «Припустимо, якийсь несамовитий лиходій передасть в неї самі невідповідні значення. Як цей м’який і пухнастий шматочок коду зможе захистити себе і зберегти всю програму від пошкодження? »Потім напишіть свій код так, щоб він перевіряв цю процедуру / функцію на наявність таких ворожих даних і захищався від них.


Розглянемо наступний приклад. Основне завдання нашої чудової космічної гри полягає в тому, щоб вбивати інопланетян і набирати бали, тому нам необхідна процедура для зміни набраних гравцем балів. Більш того, при додаванні гравцеві балів ми хотіли б викликати процедуру, яка розцвічували б підсумковий рахунок красивими іскорками. Перший варіант виглядає наступним чином:



Поки все йде нормально. Тепер задайте собі питання: «Що в цьому коді може бути не так?»


По-перше, один очевидний момент. Що станеться, якщо змінна num_points матиме від’ємне значення? Чи можемо ми допустити, щоб рахунок гравця знижувався? Можливо. Однак в описі гри я до цього ніде не згадував про можливість втрати гравцем балів. Крім того, ігри повинні приносити задоволення, а втрата балів цьому суперечить. Таким чином, ми приходимо до висновку, що негативне число очок – це помилка, яку необхідно зловити.


Цей приклад був досить простим. Існує і менш очевидна проблема (з якою я постійно стикаюся в своїх іграх). Що станеться, якщо змінна num_points буде дорівнює нулю?


Це вельми правдоподібна ситуація. Не забудьте, що після закінчення кожної хвилі ми даємо гравцеві бонусні бали в залежності від швидкості її проходження. Що відбудеться, якщо гравець буде діяти занадто повільно і ми вирішимо дати йому 0 балів? Цілком імовірно, що, працюючи над своїм кодом в 3:00 ночі, ви вирішите викликати процедуру change_score і передати їй значення 0.


В даному випадку проблема полягає в тому, що ми, швидше за все, не захочемо розцвічувати підсумковий рахунок святковими квітами, якщо кількість балів не змінилося. Таким чином, цю ситуацію також необхідно виявляти. Давайте спробуємо такий код:



Ну ось. Так набагато краще.


Зверніть увагу, що це була дуже проста функція. У ній абсолютно відсутні всякі новомодні покажчики, які так люблять використовувати молоді лихі програмісти. Якщо ви передаєте масиви або покажчики, вам ОБОВ’ЯЗКОВО потрібно передбачити виявлення помилок або поганих даних.


Переваги такого підходу не вичерпуються захистом вашої програми від перебоїв. Хороші механізми перевірки на наявність помилок також прискорюють налагодження. Припустимо, ви дізналися, що десь відбувається запис даних за межами деякого масиву, і переглядаєте свій код в пошуках місця, де це може відбуватися. Якщо в якій-небудь процедурою всі механізми перевірки є в наявності, вам не доведеться проходити її крок за кроком в пошуках помилки.


Цей підхід заощаджує масу часу і заслуговує регулярного застосування. Час – наш найцінніший ресурс.


Рада 5. «Передчасна оптимізація – корінь всіх зол», – Дональд Батіг (Donald Knuth).


Цю фразу придумав не я. Однак вона є у Вікіпедії, і тому, по всій видимості, не позбавлена ​​сенсу.


Якщо ваша кінцева мета не полягає в тому, щоб мучити людей, то при написанні програмного коду слід прагнути до максимальної зрозумілості. Простий код вимагає менше часу на написання, на розуміння при подальшому зверненні до нього і на налагодження.


Оптимізація – ворог ясності. Слід, однак, відзначити, що в деяких випадках оптимізація необхідна. Це особливо справедливо для ігор. Проте – і це дуже важливо – майже ніколи не відомо наперед, що саме необхідно оптимізувати, до тих пір, поки ви не протестуєте реально функціонуючий код за допомогою інструменту під назвою профайлер. (Профайлер – це програма, яка спостерігає за вашою програмою і оцінює час, що витрачається на виконання окремих викликів. Існують чудові програми цього типу. Знайдіть і користуйтеся.)


Кожен раз, коли я беруся за оптимізацію небудь своєї гри, я незмінно приходжу в здивування. Код, про який я турбувався найбільше, завжди відмінно працює – а гальмує код, про який я ніколи не замислювався. Оскільки я не мав уявлення про те, який код виявиться швидким, а який – повільним, час, витрачений на оптимізацію до отримання реальних даних, виявляється витрачено даремно. Насправді справа йде ще гірше – крім марно втраченого часу така оптимізація призводить до заплутування програмного коду.


Дотримуватися це правило непросто. З іншого боку, інакше воно не було б правилом. «Хороших» програмістів незграбний код дратує, навіть якщо він працює швидше.


Однак зрадійте! Після моїх довгих проповідей про те, що ви повинні витрачати більше часу на те й більше часу на це, настав рідкісний, воістину безцінний момент, коли я говорю вам, що невелика лінь цілком допустима!


Напишіть ясну і працюючу програму. Згодом у вас буде достатньо часу, щоб спаплюжити її допомогою оптимізації. Однак не робіть цього до тих пір, поки не будете абсолютно впевнені, що поступаєте правильно.


І, нарешті, раз вже ми заговорили про хворобливому, ось мій завершальний рада:


Рада 6. Чи не умничайте.


Можливо, ви чули про існування заходи під назвою International Obfuscated C Code Contest – міжнародного конкурсу по самому заплутаному програмного коду на мові C. Вся справа в тому, що мови C і C + +, при всіх своїх перевагах, дозволяють створювати кошмарно заплутаний код. Цей конкурс демонструє переваги зрозумілого коду «від протилежного» – за допомогою нагородження самих божевільних програмістів. Відмінна ідея.


З результатами цього конкурсу варто ознайомитися хоча б для того, щоб зрозуміти, який величезний збиток можна нанести за допомогою енциклопедичних знань мови програмування, доповнених відсутністю елементарного сорому. При наявності достатніх знань ви зможете втиснути десять рядків нормального коду в один рядок. Ціна за це буде невисока – всього лише повна неможливість швидко виправити помилку в цьому коді.


Основний урок полягає в наступному. Якщо створюваний код вимагає від вас детального знання хитромудрих правил пріоритету або змушує вас заглядати в останні глави будь-якої книги, щоб зрозуміти, що саме ви робите – це означає, що ви почали мудрувати.


Кожен програміст має свій допустимий рівень складності створюваного коду. Особисто я пишу свої програми так, як водить машину типова бабуся. На мій погляд, якщо ваш код на C потребує розуміння тонких відмінностей між виразами i + + і + + i, то він занадто складний.


Якщо хочете, можете вважати мене занудою. Ви маєте рацію. Однак я витрачаю на розуміння свого коду набагато менше часу, ніж міг би.


Висновок


Можливо, дочитавши до цього місця, ви думаєте: «І це все? Тільки даремно витратив час. Це ж очевидно і всім відомо. Навіщо автор все це писав? »Дуже сподіваюся, що ви думаєте саме так. Значить, ви вже самі все знаєте. Дуже радий за вас.


Однак не треба думати, що все викладене очевидно кожному. Насправді це не так. Погано написаний програмний код з’являється знову і знову – хоча без цього можна було б обійтися.


Якщо ви маєте справу з величезними обсягами програмного коду і не хочете загинути під його тягарем, я щиро сподіваюся, що мої поради вам допоможуть. Прагніть до простоти і ясності – це збереже вам масу часу і позбавить від непотрібних страждань.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*