Изборник Затворити

Мој стил, твој стил – који је бољи?

Скраћена веза: https://pedja.supurovic.net/veza/2868

Шта је стил? Ова реч потиче од старогрчке речи στύλος, која је у почетку представљала нарочиту резаљку којом се писало по воштаним таблицама. Касније је тај назив уследио за сваку врсту писаљке. А писаљки има разних и тако и разних стилова писања. Реч се временом распространила на многе делатности па и у многе језике, добијајући општије значење: начин како нешто пишемо, цртамо, стварамо, градимо и уопште, како нешто радимо.

Стил није закон и правило, стил је начин како нешто чинимо, а начина може бити разних. Да ли је погрешно писати штампаним или писаним словима, ћирилицом или латиницом, серифним или несерифним словима? Да ли је исправан дорски или јонски стил градње? Да ли је исправно Рембрантово или Пикасово сликања? Да ли је исправан карате или џиуџица? Који је стил облачења погрешан? Ниједан стил није погрешан – сви они испуњавају своју функцију, само на различите начине.

Не без основа, питање стила се везује и за питање укуса, па тако и познату изреку „укуси су различити“ можемо применити и на стил. Не постоји добар или лош, исправан или погрешан стил. Стил може бити мање или више примерен, али и то је опет углавном питање укуса и практичности.

За писање на ову тему ме је подстакао Блузменов чланак о стилу у програмирању у коме је он управо упао у замку одређивања исправног стила писања програмског кода и отишао далеко да свој стил намеће као исправан. Он је, једноставно, неке своје навике прогласио за правила и покушава да их наметне као такве. Није му успело чак ни да избегне да ниподаштава оне који се не користе његовим стилом.

Прво сам мислио да ово напишем као коментар на његов чланак али с обзиром на обим, упутније је да то буде засебан чланак.

Блузмен навео неколико примера „погрешног стила“ и у већини случајева је промашио.

Гранање програма

Најочитији је управо први пример (код Блузмена означен као (1.1). Он тврди да је конструкција

if ($a == 1)
{
    return true;
}
else
{
    return false;
}

стилски погрешна.  Он тврди како је else непотребан у оваквом коду и нуди поједностављен код

if ($a == 1)
{
    return true;
}
return false;

Могу да се сложим да пример садржи стилску грешку али не ову о којој Блузмен говори.

Његово, „исправно“, решење је у ствари лошије од оног које исправља. Грешка у оба примера је у томе  што они садрже две return команде. У процедуралном (и објектном програмирању као надоградњи) блок команди не треба да има два излаза. Намерно кажем не треба, а не не сме, јер ће код да ради и ако их има, али то није у складу са правилима логике и читкости кода. Није забрањено, али је итекако непрепоручљиво писати такав код.

Лошије од два излаза је само то да излаи буду у на различитом нивоу дубине у структури кода, а Блузмен лош код мења управо још лошијим – стављајући излазе на различите нивое.

Добар начин је да блок команди, као што има само један улаз на почетку, тако има и само један излаз који се налази тамо где и треба да буде (и где га свако очекује) – на крају блока.

Блузмен то превиђа јер му смета else у услову, сматрајући га вишком. Баш напротив, тај else значајно повећава читкост кода. Сваки програмер ће већ летимичним погледом на први пример имати представу како тај код ради, док ће у другом случају морати мало да размисли, поготово, ако тај код није овако прост, са једном до две команде па је зато очигледан. Замислите само да у Блузменовом примеру уместо те једне return команде и блоку услова стоји дужи низ. Да ли би онда било очигледно да та грана у ствари прекида извршавање целог блока? Не бих рекао. А замислите још да је структура знатно компликованија, са угњежденим условима и петљама и да негде у некој дубини стоји један return. Ко би се у томе снашао а да се добрано не удуби у анализу кода?

Друга ствар коју Блузмен превиђа је да његово решење одступа од логике која је у програмирању неопходна.

Ако имате неки услов, и ако је потребно да се ако је услов испуњен обави нека радња, а ако није, обави друга радња, онда и програмска конструкција треба да прати ту логику. Зато служи else. У if блоку се извршава код у случају да је услов испуњен, а у else  се извршава код у случају да услов није испуњен. То је логично и читко.

Блузменова конструкција ради потпуно другачије. Она извршава један код ако је услов испуњен, а код који треба да се изврши ако услов није успуњен је написан као да се извршава у сваком случају (после if блока). Наравно, тај други код неће бити извршен ако је  услов испуњен, али само зато што се у if блоку налази команда која прекида рад – дакле потпуно неочекивано и нелогично на силу се прекида извршавање кода. Наопако!!!

Ако већ говоримо о исправном стилу онда би тај код требало да изгледа овако:

if ($a == 1)
{
    $result = true;
}
else
{
    $result = false;
}
return $result;

Овакав код поштује и начела логике и читкости: блок има јасан почетак и крај, а гранање условом се врши на логичан и очекиван начин.

Блузмен оправдање за свој „правилан“ стил налази управо у намери да оствари већу читкост кода и мисли да је постиже тако што скраћује код, али потуно заборавља да је логичност кода врло битна за његову читкост – а он је логику занемарио у потпуности. Он је логику  је изврнуо да би постигао скраћење кода.

Шта је постигао – да неко други ко анализира код, мора да се бави том изврнутом логиком уместо стварном логиком посла који програм треба да уради.

Ако већ причамо о стилу и читкости, ја у ствари више волим да овај код напишем овако:

if ($a == 1) {
  $result = true;
} else {
  $result = false;
}
return $result;

Мени овакво писање са витичастим заградама стопљеним у код делује прегледније али, наравно, немам намеру некога да убеђујем да је такав стил бољи.

У следећем примеру (1.2), Блузмен прави потпуно исту грешку, само још очигледнију. Други пример му је у ствари исти као и први, само има више кода. Он поново „правилан начин писања кода“ заснива на неприкладном изласку у сред кода.

Блузмен се води принципом да на почетку блока провери да ли су сви неопходни услови испуњени, па ако нису да одмах прекине извршавање кода присилним изласком из блока. То, наравно, није погрешно, али свакако то не сме бити наметано као исправан стил.

Ако се држимо правила читкости и логике, онда је нормално да се на почетку кода провери да ли су сви предуслови испуњени, ако јесу, да се уради посао који је потребан, а ако нису да се тај проблем на одговарајући начин регулише, али не насилним изласком у сред блока, већ регуларним, на крају блока.

Овај његов пример подстиче на још једну дилему: да ли услов треба да буде позитиван или негативан, или да ли треба у if проверавати да ли нешто јесте или нешто није. Мислим да ту треба тежити логици посла, а if треба да следи оно што је важније, односно ономе због чега је код и написан, а споредне токове ставити у други план.

Пример:

if ([preduslov je ispunjen]) {
  [uradi glavni posao]
} else {
  [obradi izuzetak]
}
return;

Овај код је читак јер он води ток према логици посла који треба урадити. Очекивано је да, пошто код ради неки посао, до њега се долази када су предуслови испуњени. Зато if конструкција главни посао ставља у први план, а изузетак, да предуслови нису испуњени ставља у други план. Некоме ко чита овакав код, ствари су постављене логично и очекивано те ће му бити лакше да се снађе.

Ако се уради обрнуто:

if ([preduslov nije ispunjen]) {
  [obradi izuzetak]
} else {
  [uradi glavni posao]
}
return;

правимо компликацију – изузетак стављамо  у први план и отежавамо ономе ко чита програм, јер он сада мора да тражи где је важан део кода у блоку. Наравно, ни овакво писање није погрешно. Програм ће радити у сваком случају, а понекада је и добро овако урадити када је важно нагластити изузетке. Ипак, ја бих се пре определио за први начин, који у први план ставља главни посао.

Ситуација може бити и обрнута, ако услов није испуњен тада треба да се ради главни посао. И тада бих  if услов прилагодио томе да се главни посао ставља у први план.

Још једном наглашавам – важно је избегавати да се из блока излази на више места. Излаз треба да буде један – онај на крају блока.

Скраћено гранање програма

Блузмен је из рукава извукао још једну занимљиву конструкцију у PHP-у  скраћену структуру условног израза (тачка 1.6 у његовом чланку).

Наиме, због природе послова у којима се PHP језик користи, а то је углавном генерисање HTML кода, врло је честа потреба да се HTML код генерише у зависности од неког услова. То наравно може да ради и класична if команда али је она због своје структуре незграпна и чини код који је и иначе састављен од  мешавине PHP и HTML још нечиткијим. Зато је уведена конструкција условног израза која има облик

[uslov] ? [izraz1] : [izraz2]

Израз се састоји од услова, вредности коју израз враћа ако је услов испуњен и вредности коју израз враћа ако услов није испуњен. Очигледно је ово много краће и лакше за коришћење. Погледајте овај код:

<div class="red">
<span class = "naziv">Status:</span>
<span class="vrednost"><?php (!empty($m_status) ? $m_status : "[nije definisano]"</span>
</div>

Довољно је „збрчкан“ мешањем PHP и HTML кода, а замислите како би тек изгледало да је уместо условног израза морао да се углави обичан if.

Међутим, Блузмен ову конструкцију препоручује уместо класичног if  и када за тим нема потребе. Када пишете чист PHP код, онда треба да користите класичан услов јер је то оно што је очекивано и природно и читко. Овај скраћени условни израз користите првенствено када мешате PHP i HTML или у неким збрчканим изразима којима склапате неки стринг од много делова па је једноставније да употребите скраћене условне изразе уместо да компликујете са if конструкцијама.

Именовање променљивих, функција, класа…

Блузмен се у свом примеру 1.3 бави именима променљивих (он их зове варијаблама, што није погрешно али и то указује на његов стил). Сасвим исправно, залаже се да се не користе криптична имена од једног или пар слова из којих није логично чему променљиве служе.

Давно је прошло време програмских језика у којима су променљиве могле да имају само једно слово у имену, или је дужина имена променљиве утицала на потрошњу меморије. Одавно није тако и не постоји ниједан разлог да променљивама не дајемо јасна имена.

Постоји у ствари један изузетак. Задржао се обичај да се у бројачким петљама, за имена бројача користе једнословна имена и то чак првенство имају слова i, n, m, j и k. Многим програмерима је необично да виде другачије именоване променљиве ових бројача.

Но, иако је именовање променљивих витално за читкост програма Блузмен томе посвећује необично мало пажње. Где су практични савети како то радити?

Пре свега, именовање се не односи само на променљиве него на све елементе кода којима програмер сам може да надене име. То су и променљиве, и фунције, и типови и класе и имена датотека модула и ко зна шта све још један програмски језик може да садржи.

Добар је обичај увести одређена правила у начин давања имена. Та правила можемо узети од некога ко их је већ разрадио, али их можемо и сами осмислити. По правилу, сваки програмски језик има већ неку општу препоруку око именовања и уопште стила писања кода, и добро је придржавати се тога, јер тако други програмери могу лакше да прате ваш код,  већ навикнути на усвојени стил писања.

Оно што је важно, то је да се усвојених правила придржавамо. Чак и ако одступимо од уобичајених правила, уводећи своја, ниједном програмеру неће бити тешко да им се прилагоди ако су она доследно примењивана.

Први проблем са којим се срећемо то су имена која се састоје од више речи. Неко то решава тако што уместо знака размака ставља доњу црту, неко тако што не одваја речи подвлаком, али зато свако прво слово у речи пише као велико слово. О коришћењу великих и малих слова у називима треба посебно добро размислити ако се користи неки програмски језик који прави разлику између њих (на пример Ц, Ц# и слични језици).

Код именовања посебних елемената као што су типови, класе, структуре, модули, стандардни објекти и слично, добро је да у имену користите префикс или суфикс који описује тип објекта. Тако остављате слободан простор да користите иста имена за променљиве али без ознаке типа али и избегавате евентуалне проблеме ако елементе различитог типа пожелите да именујете исто. Префикс ће обезбедити јединственост имена.

Коришћење променљивих

Када смо баш код променљивих ево неких препорука:

Правите разлику између локалних променљивих, параметара и глобалних променљивих. Ја имам обичај да локалним променљивим дајем префикс m, параметерима p, а глобалним променљивим g. Тако је у коду лако препознати порекло и виљивост неке променљиве.

Неретко,наићи ћете и на давање имена према типу. Тако програмер наглашава којег типа треба да буде податак у променљивој. Ово користим, али нисам доследан. Углавном назначавање типа употребим када је то важно: рецимо, када се врши трансформација података из једног типа у други, имена променљивих која садрже и ознаку типа значајно повећавају разумљивост кода.

Променљиве користите у њиховом домену, пре свега о томе водите рачуна када им мењате вредност.

Са локалном променљивом можете радити шта год желите јер је она битна само у блоку у коме је и дефинисана – она само у том блоку и постоји.

Параметар је боље да не користите у блоку него на почетку блока његову вредност доделите локалној променљивој и њу даље користите. Иако је Блузмен чврсто против „неотребног“ коришћења променљивих (види тачку 1.7. у његовом чланку), брзо ћете се уверити колико је то практично.

У току развоја, често ћете доћи у ситуацију да је потребно да измените вредност коју сте добили као параметар, али и да морате сачувати оригиналну вредност. Зар није логичније да параметар увек задржава своју вредност (иако је параметар у суштини локална променљива, он садржи вредност коју сте добили споља, изван блока кода), а да за потребе кода имате локалну променљиву којој на почетку доделите вредност из параметра а даље можете са њом да радите шта год желите?

Ако не урадите тако, већ параметар користите у коду, па вам затреба да сачувате његову почетну вредност, тада ћете свакако морати да направите нову променљиву, али са потпуно обрнутом логиком – та нова променљива ће да чува вредност јер сте сам параметар већ употребили у коду.

Нарочито водите рачуна о мењању вредности глобалних променљивих. У принципу коришћење глобалних променљивих треба избегавати јер оне итекако могу да направе забуну, али ако имате јасна и чврста правила онда оне могу много да олакшају посао.

Прво правило треба да буде да као глобалне променљиве користите само оне које су заиста неопходне да буду глобалне – обично су то неки глобални статуси апликације који треба да буду доступни у сваком тренутку у било ком делу програма.

Друго, глобалне променљиве третирајте као константе. То значи, да им на почетку програма доделите вредност и да ту вреднот даље не мењате. Само у изузетним случајевима, дозволите мењање вредности глобалних променљивих.

Постоји још једна ствар везана за глобалне променљиве. У неким језицима, као што је на пример PHP, иако сте неку променљиву дефинисали као глобалну, опет у свакој функцији морате да је редекларишете као глобалну да бисте је заиста могли и користити. Уме да буде заморно ако низ глобалних променљивих који вам је неопходан сваки пут морате да редекларишете. Зато је згодно користити једну глобалну променљиву која је сложене структуре. У PHP-у можете направити глобалну променљиву која је у ствари асоцијативни низ, па у њу стављати све што вам је потребно. Тако увек треба да редекларишете само једну променљиву. И други програмски језици имају сличне конструкције.

Сваку променљиву иницијализујте. Неки програмски језици вам не дозвољавају да користите променљиву, ако је пре тога не дефинишпете и дате јој почетну вредност. Неки програмски језици су лежернији и управо у њима је врло важно да доследно свакој променљивој на почетку дате неку почетну вредност иначе ћете се врло брзо наћи у небраном грожђу, пошто ће, уместо вас, сам компајлер или интерпретер да додели неку вредност по свом нахођењу.

У функцијама, добар је обичај да дефинишете променљиву која ће садржавати резултат обраде који функција враћа. Добро је да имате неко универзално име за такву променљиву (на у PHP-у на пример за ту намену увек користим име $m_result, тако да ми је лако да се снађем приликом анализе кода – свака функција има ту променљиву). Delphi (Paskal uopšte) вас на то чак и приморава, но то не изненађује јер је он један културан и господски програмски језик који ће увек стајати на посебном месту какву год лествицу програмских језика правили.

Помоћне променљиве

Немојте избегавати помоћне променљиве ако ће то повећати читкост кода. Број променљивих не утиче нарочито на потрошњу ресурса (осим ако заиста не садрже огромну количину података) те нема разлога штедети на њима, нарочито зато што су помоћне променљиве по правилу локалне. Из неког разлога Блузмен се оштро противи „непотребном“ коришћењу локалних променљивих (види његову тачку 1.6).

Ако код ради неку компликовану обраду, слободно ту обраду поделите на више команди, чак и ако можете да их изведете као једну команду (рецимо нека компликована математичка формула). Међупроменљиве ће код учинити читкијим, а и лакшим за тестирање.

Ево примера читкости на практичном задатку. Ради се о задатку који се ради већ у првим корацима у учењу програмирања. Задатак се зове замена вредности двема променљивима. Приказана су три могућа решења.

– прво решење користи две помоћне променљиве

– друго решење користи једну помоћну променљиву

– треће решење не користи помоћне променљиве уопште

Решење 1

<?php

$a = 5;
$b = 3;

$c = $a;
$d = $b;

$a = $d;
$b = $c;

echo "a = $a; ";
echo "b = $b";

?>
Решење 2

<?php

$a = 5;
$b = 3;

$c = $a;

$a = $b;
$b = $c;

echo "a = $a; ";
echo "b = $b";

?>
Решење 3

<?php

$a = 5;
$b = 3;

$a = $a + $b;
$b = $a - $b;
$a = $a - $b;

echo "a = $a; ";
echo "b = $b";

?>

Које решење је најчиткије? Прво има превише кода због манипулације са вредностима превише променљивих. Треће решење има најмање кода али ћете разумети само ако га анализирате. Друго решење, са једном променљивом, није најкраће, али је јасно и очигледно на први поглед – најчиткије је.

Закључак: не треба претеривати са променљивама, али не треба ни штедети по сваку цену.

Да ли је променљива празна?

У тачки 1.4 у свом чланку Блузмен препоручује коришћење фунције empty() за проверу да ли је променљива празна. Опет, он то ради уз необично уопштавање и занемаривање врло битних ствари.

Њему је разлог за коришћење функције empty() начин да се обраде ситуације када добијемо вредност променљиве која није исправна (на пример није дефинисана, садржи NULL, или садржи неки други тип вредности). Овакво размишљање може да буде опасно и да вам направи такву збрку да престанете уопште да користите ову функцију.

Шта се може десити ако уместо

if ($string == ''){}

употребите

if (empty($string)){}

Пре свега, empty() ће сакрити да ли је променљива уопште дефинисана, да ли задржи NULL или заиста садржи празан стринг. Као што сам већ рекао, за сигуран код неопходно је да све променљиве буду дефинисане и да им се доделе исправне почетне вредности. Функција empty() ће сакрити случај да сте пропустили да исправно дефинишете променљиву.

Друга ствар, функција empty() не разликује типове. Она проверава да ли променљива има неку вредност која се за тај тип сматра празном вредношћу. Ако променљива има вредност, али је она погрешног типа, функција empty() ће и то да занемари.

Ево примера: шта ако променљива $string садржи вредност 0 (нула, нумерички). Функција empty() ће вратити true, што јесте тачно за нумерички тип, али ви ту очекујете стринг, а стринг ‘0’ није празан стринг. Интересантно, исто се дешава чак и ако променљива $string садржи вредност ‘0’ (цифра нула као стринг). И тада ће empty() вратити true, што тек није тачно, јер празан стринг је празан стринг, а не ‘0’.

Ево пробајте:

<?php
$a = "0";
echo "empty " . empty ($a) . "<br>";
echo "== '' " . ($a == "") . "<br>";
echo "isset " . isset ($a) . "<br>";
echo "vrednost: $a";
?>

Грешке које се на овај начин могу направити нису очигледне и одузимају много времена. Зато треба водити рачуна да себи не отежавамо њихово проналажење.

Функцију empty() треба користити управо када се проверавају и постављају почетне вредности променљивих. На пример ако функција добија параметар, један од начина да се добијена вредност доведе у опсег дозвољених вредности је и да се провери функцијом empty().

Кад смо код ове функције никако не треба заборавити да се напомене још једна функција isset(). Она прави разлику између дефинисане и недефинисане вредности. Т0 је нешто што често треба да се проверава – не да ли је променљива празна него да ли је променљива дефинисана.

Функција у услову петље

У тачки 1.5 Блузмен се бави коришћењем функција у услову петље и са правом скреће пажњу на коришћење функција које сваки пут дају исти резултат.

Дакле не

while ($i < strlen ($string))
{
    // neki kod
    $i++;
}

већ

$len = strlen ($string);
while ($i < $len)
{
    // neki kod
    $i++;
}

наравно, под условом да се у петљи не мења вредност променљиве $string.

Међутим, Блузмен иде даље па показује да се код може додатно скратити (он очигледно поистовећује скраћење кода са повећањем читкости) тако што ће се користити бројач уназад, па тако нема потребе за писањем услова у петљи:

$len = strlen ($string);
while ($len--)
{
    // neki kod
}

Овакав код је добар да се неки надобудни млади програмер (притом не мислим на Блузмена, он је стара кајла) доказује у друштву како је домишљат, али са читкошћу кода нема много везе. Напротив, оваквим кодом ми сакривамо логику програма и приморавамо онога ко чита програм да га анализира да би видео шта се дешава. Овакав код, иако сасвим исправан, није уобичајен и не следи природну логику.

Бројање уназад није природна логика те га користите само онда када је то заиста неопходно, на пример, ако обрађујете индексирани низ и из њега избацујете неке елементе, тада треба да идете до краја низа ка почетку да би ваш бројач био у складу са стварним редним бројевима елемената у низу.

Илустрације ради, позивам вас да решите следећи задатак. Напишите у PHP-у програм који рачуна факторијел задатог броја, али под једним условом – не смете користити ниједну условну команду у програму. Да ствар буде занимљивија, онај ко да најкраће решење (најмањи број команди) добија на поклон мајицу. Решење не треба да садржи кориснички интерфејс него да буде чист код – дакле на почетку се променљивој у коду додели број за који треба израчунати факторијел, а на крају кода се са echo испише резултат. Ако се појави више исправних решења са истим бројем команди, награда иде ономе ко је пре послао решење. Решења немојте слати као коментаре јер ће бити одмах видљива, него ми их пошаљите као приватну поруку. Рок за слање решења је три дана, након кога ћу сва решења објавити овде и објавити ко је добио награду.

Циљ овог задатка није да се доказујете као програмери, на крају крајева ово је тривијалан задатак, већ да се види колико је такав код нечиткији и да је неопходно анализирати га да би се видело шта ради.

Коментари у коду

Блузмен је поропустио да у свом чланку о стилу дође и до писања коментара, а они су итекако одраз доброг стила.

Коментари су, кратко речено, неопходни. Ма колико ви писали јасан и читак код, он може да буде довољно сложен да није очигледно шта и како ради. Зато служе коментари, да тамо где су ствари неочигледне скренете пажњу и помогнете у разумевању кода.

Коментаре не пишете само за друге. Пишете их и за себе, јер након неколико месеци или година, ако морате да се вратите неком свом коду да га преправите, бићете у истој позицији као и неко ко га први пут види – мораћете да га анализирате да бисте видели шта код ради.

Златно правило писања коментара је да их треба писати само у неопходном обиму – дакле ни премало ни превише. Премало коментара не помаже довољно, а превише коментара одмаже јер затрпава код. Нарочито немојте писати коментар који објашњава нешто што је више него очигледно.

Трудите се да у сваку датотеку на врху као коментар упишете неке основне податке: чему код у њој служи, шта је неопходно да би код могао да ради, ако зависи од неких библиотека, када је започето писање кода, када је урађена последња измена, а добро је и да постоји део где се уписују напомене о свим битнијим изменама у коду, када су урађене и ко их је урадио.

Када уводите глобалну променљиву добро је да уз њу напишете и коментар којим објашњавате њену намену.

Ако дефинишете класу, напишите чему она служи.

Код дефинисања функције, обавезно опишите њену намену, и опишите параметре који јој се прослеђују са типовима и дозвољеним вредностима, као и опис вредности коју функција враћа.

У самом коду, на месту где знате да може бити нејасно, а нарочито тамо где сте и сами провели доста времена мозгајући како да нешто решите, напишите у кратким цртама шта и како сте урадили да не морате поново да цео проблем анализирате и решавате из почетка.

Кад год у коду радите нешто неуобичајено, као што су, на пример, ови Блузменове насилни изласци у сред блока команди, или бројачке петље које броје уназад и слично, ставите у коментару напомену, макар да би приликом летимичног прегледа кода то место било уочљивије.

И празан ред може да буде коментар. Неке целине у коду одвојте празним редом, исто као и што у обичном тексту одвајате пасусе. То може значајно да повећа читкост чак и ако та целина нема засебан коментар. Добри кандидати за то су гране у коду, петље, сложени математички изрази које сте због читкости поделили у више целина и слично.

Редослед извршавања операција у изразима

Ово је још једна тема коју је Блузмен прескочио. Свако је још у основној школи научио да се у математичком изразу прво врши множење и дељење а тек затим сабирање и одузимање. Када учите неки програмски језик, обавезно и то дође на дневни ред.

Неки програмери се ослањају на редослед извршавања операција у изразима. Неко ће написати израз a / b + c / d и добити тачан резултат, а ја ћу увек написати (a / b) + (c / d) и добити такође тачан резултат.

Не стављам ја те заграде јер не верујем програмском језику, него зато што ми је тако код знатно читкији. Ако је израз компликован и дуг, и поготово ако се налази рецимо у услову, онда ћу додатно поделити целине у њему по редовима, урадити увлачење по нивоима заграда и тако учинити да ми он буде јасан и читак.

Исто препоручујем и вама.

Назубљивање кода

Увлачење редова по дубини у структури кода је толико постало нормално и уобичајено да када некога питате шта је важно за читкост кода, то ће лако да заборави да наведе.

Назубљивање се врши једнотавно и већина програмских уређивача текст то раде аутоматски. Како ушете у нови ниво дубине кода тако се цео текст ко јиприпада том нивоу увлаши мало удесно. Све команде које су у истом блоку, на истом нивоу су подједнако увучене. Тако се јасно види који код припада ком блоку, што је изузетно битно када је код компликван и има много гранања, и петљи.

Код назубљивања је добро корситити знакове размака уместо табова и ако се у пракси подједнако срећу оба приступа. Табови су згодни јер свако може даподеси „ширину“ увлаке како му одговара, али зато знакови размака имају предност баш у томе што је ширина константна независно у ком програму се код прегледа. То је важно када се код приказује на начин када је тешко или немогуће контролисати ширину таба (рецимо на вебу), поготво што су често ширине табова подразумевано подешене на неке претарано велике вредности па то у ствари код учини нечитким јер постане превише увучен.

Колико увлачење је довољно? Има разних укуса а ја сам нашао да ми највише одговара ширина од два знака. то еј довољно да е увлачење лепо види а ако је структура компликована текст не бежи превише у десно.

Наравоученије

Не бих волео да неко овај мој чланак протумачи као некакав напад на Блузмена. Напротив, њега ценим и као програмера а и на другим пољима.

Својим чланком ме је само подстакао да се позабавим темом која је битна за сваког, нарочито свежег и неискусног програмера. Навика је се тешко ослободити, а ако су оне лоше, то уме да буде проблем. Блузмен је у свом чланку потенцирао као исправне неке начине писања кода који су далеко од доброг, а у ствари су резултат његових лоших навика. Само зато што је он на то навикао, чини му се да је то и добро. А није тако. При том, Нисам настојао да доказујем да Блузменов стил не ваља, већ да се он не може прихватити као правило и једини исправан начин писања програма.

Покушао сам користећи његове примере као основ да покажем шта заиста јесте читко, јасно и прегледно писање кода. Сигурно је да и ја имам неке своје навике које нудим као „бољи“ начин, чак и ако тога нисам ни свестан. Вероватно сам и ја, као и Блузмен, у свом чланку прескочио да се осврнем на нека питања на ову тему. Али зато су ту читаоци, па ће и они реаговати на мој чланак, као што сам ја на Блузменов.

Још једном ћу да нагласим да је стил, као и укус, ствар укуса. Неко више воли овако, неко онако. Крајњи резултат је исти – програм ради свој посао. Питање стила се потеже тек ако више програмера ради на истом коду, јер они треба да разумеју оно што је неко други радио да би могли да наставе да раде на том коду.

Ако већ мора да се прави поређење који је стил бољи, управо то треба да буде критеријум – колико неки стил писања кода доприноси да се други програмер брзо и лако упозна са тим кодом, да га разуме и да може да настави да ради на њему.

Начин писања кода је нешто што програмера прати у целој његовој каријери. Напредовањем, мењањем посла, учешћем у разним тимовима, програмер се среће са различитим стиловима и мора да их се придржава. Негде ће му његов лични стил толерисати, а негде неће. Ипак, најлакше је прихватити стил програмирања који је читак, лак за разумевање и праћење и који прати логику којом се проблеми и иначе решавају. Са таквим стилом ће вас свугде лакше прихватити.

Свој стил изградите тако што ћете проучити препоруке за програмски језик који користите и стилове којим пишу други програмери. Прихватите оно што је уобичајено и оно што вам се чини сврсисходним. Да ли је ваш стил добар утврдићете када се први пут будете срели са својим кодом након дужег времена. Колико је стил програмирања добар, толико ћете се лако вратити том програмском коду.

И на крају, немојте бити превише конзервативни. Не инистирајте на стилу по сваку цену. Некада је добро и одступити од њега, ако ће то у конкретном случају поједноставити код или га учинити читкијим. Нарочито немојте претерано инсистирати на својим навикама ако радите у тимовима. Обично у тимовима постоје начелна правила у писању кода али се на њима не инсистира по сваку цену. Зато, немојте ни ви.

21 Comments

  1. Peđa

    Због поређења, изабрао сам само једну платформу, ону која је најраспрострањенија – PHP. За ову сврху не могу се поредити решења у два различита програмска језика. А и не знам Python.

  2. Peđa

    Чланак је написан ћирилицом. Твој надимак на енглеском је пресловљен на ћирилицу. Латинична верзија чланка се добија пресловљавањем са ћирилице на латиницу.

  3. bluesman

    I u latiničnoj verziji je pogrešno, da sam hteo takav nick kako me ti krstiš – sam bih ga izabrao takvog. Ja to kapiram kao omalovažavanje, ok, ako nećeš da promeniš – onda ništa … ignorišem te.

  4. Peđa

    Твоја воља. Надимак ти је транскрибован по правилима транскрипције, из енглеског језика у српски. Ако ти то смета, пиши Матици Српској, можда измене правопис да ти учине.

  5. Ivan

    Slazem se sa tvojim tekstom, iako nisam citao bluesman-ov.

    Pre svega, slazem se da mnogi programeri upadaju u zamku prerane optimizacije koda, koja je po staroj izreci: izvor svog zla. Mada, u nekim primerima. Ima smisla naglo prekinuti dalje izvrsavanje funkcije i procedure, kako bi se ustedelo na procesorskim ciklusima i vremenu izvrsavanja koda.

    Naravno, pre nego sto iko i odluci na takav potez. Potrebno je da, posto zavrsi pisanje koda i utvrdi da mu aplikacija radi dobro ali sporo, odluci da je optimizuje! Uradi dobru analizu. Po mogucstvu, koristiti neki profiler za te stvari.

    Najbolji stil je onaj koji je najcitkiji i najlogicniji. Optimizacijom koda dolazimo do onog koda koji je nezgrapan i tesko citljiv, ali to je nesto sto prihvatamo kako bismo ustedeli koji ciklus vise. Naravno, tek na kraju development process-a i to samo onde gde je neophodno! Uz dosta komentara i veoma oprezno.

  6. Peđa

    Свакако, у програмирању је дозвољено све што ради посао. Ако ће се добити бољи резултат (брзина, мања потрошња ресурса и слично) онда се свакако не треба слепо држати уобичајеног читког стила.

  7. Nikola

    Peđa, interesuje me kako bi izgledalo rešenje zadatka kog si postavio u PHPu, jer je baš trivijalno u Pythonu i Javi, pa rekoh da ne propuštam nešto? Hvala unapred.

    • Peđa

      Јесте тривијално, управо зато сам то и поставио као задатак. Добио сам само једно решење, али никако да стигнем да то пустим на блог. Потрудићу се да буде што пре.

  8. Peđa

    Ево и резултата наградне игре. Борко Куцин, који је први и једини послао решење освојо је симболичну награду у облику мајице. Она ће му бити послата поштом. Честитам Борку и захваљујем му се на труду.

    Наравно, задатак је био тривијалан, па нисам ни очекивао да ће неко послати погрешно решење, мада сам, с обзиром на читаност овог чланка очекивао да ће се још неко јавити са својим решењем. Било би интересантно упоредити их јер има више начина да се уради овај задатак.

    Но, да се вратим на тему. Борково решењ ћу искористити да покажем да краћи код не мора да буде и читкији.

    Ево његовог решења (пример је израчунавање факторијела од броја четири):

    <?php
    
    $n = 4;
    $result = 1;
    while ($n--) {
      $result=$result*($n+1);
    }
    echo $result;
    
    ?>
    

    Да бисте разумели како овај програм ради морате га мало анализирати, јер начин његовог рада ниеј очигледан зато што не прати уобичајену дефиницију факторијела.

    Еко како изгледа школско решење:

    <?php
    
    $n = 4;
    $result = 1;
    if ($n > 1) {
      for ($i = 2; $i <= $n; $i++) {
        $result = $result * ($i);
      }
    }
    echo $result;
    
    ?>
    

    Оно јесте дуже, али прати дефиницију факторијела тако да је програм очигледан и лако је разумети како ради.

    Због једноставности, оба програма су написана са претпоставком да је улазна вредност позитиван цео број тако да се не врши провера.

  9. Nikola

    Peđa,

    Koliko vidim, obe verzije koriste neku vrstu kondicionala, bilo da je to u while petlji ili eksplicitni if statement, što ceo zadatak pravi baš trivijalnim. U tom slučaju imate i kraću verziju (PHP 5.3)

    <?php
    $factorial = function($n) use (&$factorial) {
    return $n ? $n * $factorial($n – 1) : 1;
    };
    echo $factorial($_SERVER[1]);

    dok verzija bez bilo kakvog kondicionala prelazi 100 LOC.

  10. Peđa

    Не можемо петљу сматрати баш искључиво условним кораком. Ипак је то петља и њен саставни део је услов за излаз из петље.

    Не знам шта је 100 LOC.

    Тривијалност овде није поента. Поента је у томе да како се „унапређује“ код, тако он постаје непрегледнији.

    Борков пример то довољно добро илуструје, твој још боље, а верујем да би неко могао да смисли још запетљаније решење и тиме само још боље илустровао оно што је и било циљ да се покаже: да избегавање класичног гранања у коду само зато да би оно било избегнуто, нема неку сврху, већ само прави код нечитљивијим, те је то уопште узевши, лоша пракса, а не једини исправан начин писања програма, како је то Блузмен у свом чланку представио.

  11. Nikola

    LOC (LoC) – lines of code. Slažem se da je sastavni deo svake petlje izlaz iz petlje, ali školski kad se kaže uradi nešto bez kondicionala, to znači uradi bez bilo čega što implicira kondicional – to znači i bez petlji :) Drugim rečima, jednostavan inkrementer možete napisati i ovako (PHP 5.3):

    <?php
    $n=0;
    for(;;){
    if($n == 10){goto a;}
    $n++;
    }
    a:
    echo $n;

    gde sada eksplicitno uključuje if statement (kod gore NIKAKO ne koristiti). Sve ovo na stranu, u srednjim i većim softverskim firmama, jedino merilo dal je kod dobar ili nije jeste pozitivan ishod code review-a. Kad si junior software developer, skoro sigurno je da code review nećeš proći iz prve :) Tu je bitna čitljivost (u smislu brzog skeniranja), komentari, samodokumentovanost, dobre prakse (npr. ne nazivaj promenljive kriptičnim imenima) i još ogroman broj drugih stvari. U PHP-u koji ima GC „ugrađen“ u sebe obično nije bitno da li uvodiš još 10 lokalnih promenljivih, pošto će one da budu sakupljene kad isteknu. Zlatno pravilo: ako imaš performance problem, uvek je lakše refaktorovati i optimizovati čitljiv kod nego kriptičan; da li je nešto kriptično ili nije obično se određuje dizajn guidelineovima te firme. Izmedju školskog i kriptičnog koda biram školski u svako doba, pod uslovom da je taj neko dobro naučio tu školu :)

  12. Peđa

    Петља не може да функционише ако не постоји услов за излаз из ње тако да је беспредметно инсистирати на томе да петља не сме садржавати услов.

    Тако и овај твој пример са „празним“ for опет садржи услов, иако се он не види и не користи па си од њега направио замену за goto. У принципу ти он није ни треба, јер ако си се већ окренуо као goto команди, онда for нема сврху.

    На крају, и ово твоје је добар пример, јер показује како ствари почињу да постају нечиткије како се одступа од школског приступа.

  13. Nikola

    Moguće je da sam napravio zbrku sa korišćenjem reči petlja :) Ne insistiram da petlja (petlja u širem smislu – for, while) ne sme sadržati uslov, već da svaka petlja koja sadrži uslov jeste kondicionalni blok koda (verujem da je ova rečenica redudantna :)). U akademskim primenama (kada kažeš školski), kada se kaže bez kondicionala, misli se bez if-else konstrukta, komparatora, break iteratora, short circuit conditiona kao i mase drugih language-specific konstrukta, osim ako nije drugačije naznačeno. Takođe, u kodu gore, umesto goto a; za ispis sam mogao da iskoristim i echo $n; exit(); Potpuno je isto da li ćeš koristiti while(true) pa break-out uslov ili while(neki uslov) pa petlja prestaje kada uslov prestane da važi. Dalje, formalno, for i while petlje ne mogu da postoje bez uslova, ali taj uslov može da bude nepromenljiv, kao što je while(true) petlja, te iz petlje nema izlaza. Takođe, evo petlje (u užem smislu) bez uslova (PHP 5.3):

    <?php
    $u = 0; // Školski :)
    h:
    $u++;
    echo $u.PHP_EOL;
    goto h;

    nije preterano korisna petlja, al je petlja. Takođe, biti školski jeste biti maksimalno egzaktan, barem kada je postavka zadatka u pitanju :) [Disclaimer: ne tvrdim da je moj kod ili pristup školski (čak suprotno) :)]

    • Peđa

      Управо сам и мислио на такво коришћење goto наредбе, које искључује потребу за for наредбом.

      Што се тиче дискусије у вези значења израза „услов“ и петљи, не видим сврху томе. Сасвим је јасно да је услов у петљи неопходан део петље и не може се тако ригидно посматрати као условни корак јер је то само део петље.

      Ако хоћеш да кажеш да ти ниси послао решење зато што си мислио да не смеш користити петљу, требало је само да питаш, мада мислим да је било сасвим очигледно да је некаква петља у решењу задатка неопходна, а поготово када се узме у обзир да је речено да се ради о тривијалном задатку, то би требало да буде сасвим јасно.

      Но, и поред тога, твоји примери су веома корисни, јер упућују на могуће приступе у програмирању који дају решења, али који никако не могу да се сматрају читким, што и јесте био циљ чланка па и целе дискусије.

  14. Nikola

    Ne treba mi majica ako me to pitaš :) U raspravu sam ušao iz čisto sebičnog razloga moram priznati, a to nije majica :) Hteo sam da saznam kako u jeziku kao što je PHP (koji ne znam, moj prof. background je C i Java btw.), while petlja se ne smatra >i< kondicionalnom funkcijom, bez obzira što joj to nije primarna namena. Realno, ti možeš da radiš poređenje sa while funkcijom, bez obzira koliko je to besmisleno i to možeš da uradiš u istom kontekstu u kom bi koristio if statement. Razumem da tema ovog blogposta nije ovo o čemu ja drvim sada, ali oko osnovnih konstrukta i funkcija ne sme biti pregovora: treba tačno znati kako koji izgleda i šta radi. Ako bi ti neko na ispitu iz uvoda u programiranje (ICSP) postavio zadatak napravi faktorijel BEZ bilo kakvih kondicionala, ne bi prošao dobro ako bi koristio for ili while loop :) Ako si raspoložen, pokušaj da uradiš ovaj zadatak tako.
    Btw, na datavoyage.com imaš iframe na dnu strane koji nije ubijen, pretpostavljam da je to ostatak one zaraze od pre.

  15. Peđa

    Док за while и могу донекле да прихватим да се тумачи као условни корак, за for не могу. Она је по дефиницији бројачка петља и у њеним изворном смислу услов и не постоји – задаје се почетна и крајња вредност бројача и корак бројања. У цеоликим језицима је њена синтакса проширена тако да садржи и услов тако да јој је и функционалност проширена. Ипак, ја је и даље гледам као бројачку петљу.

    И ето, пао бих на испиту. :)

    Хвала за инфо за iframe. Кренуо је нови талас напада. Очигледно се нисам ослободио пошасти.

  16. Boban Jovanović

    Stil koji godinama koristim, a koji kombinuje kompaktnost i čitljivost koda je oblika:

    if ($a == 1) {
        $result = true;
    }
    else {
        $result = false;
    }
    return $result;
    

    Pravilo je da svaki blok počinje velikom zagradom u istom redu sa komandom, a završava se u posebnom redu, na početku.

    • Peđa

      Морам признати, врло необичан приступ, који први пут видим а није ми баш логичан и не изгледа ми читко.

Оставите одговор на Peđa Одустани од одговора

Ваша адреса е-поште неће бити објављена. Неопходна поља су означена *

Попуните израз тако да буде тачан: *

Ово веб место користи Акисмет како би смањило непожељне. Сазнајте како се ваши коментари обрађују.