Když programátor napíše blok kódu či složitější podmínku, tak má tendenci k vytvořenému kusu kódu napsat vysvětlující komentář. Z hlediska pochopitelnosti kódu je to samozřejmě lepší, než kdyby se čtenář musel snažit pochopit význam analýzou kódu. V dnešním příspěvku si však ukážeme lepší alternativu: místo bloku kódu s komentářem napíšeme funkci.
Mějme následující kód napsaný v jazyce C. Převzal jsem jej z jednoho starého projektu, na kterém jsem kdysi dělal. Jedná se o funkci pro vložení symbolu na vrchol zásobníku pro precedenční analýzu v překladači. Zkuste si jej pročíst.
// prec_stack.h void prec_stack_push(PrecStack *s, Item item); // prec_stack.c void prec_stack_push(PrecStack *s, Item item) { if (s->top == NULL) { // The stack is empty, so we initialize it with the first item. s->top = prec_stack_create_item(item); s->top->next = s->top->prev = NULL; } else { // There is already an item on the stack, so we add another one. struct prec_stack_item *new_item = prec_stack_create_item(item); new_item->next = s->top; new_item->prev = NULL; s->top->prev = new_item; s->top = new_item; } // If we have pushed a terminal, we need to store this piece of information // for the precedence analyzer. if (item.type == T_TERM) { s->topmost_term = s->top; } }
Zásobník je implementován obousměrně vázaným seznamem. Při vložení položky se musíme dívat, zda je zásobník prázdný či nikoliv. To se zjistí tak, že se podíváme, zda na jeho vrcholu nic není (s->top == NULL
). Pokud je zásobník prázdný, tak vložíme první položku, jinak vložíme další položku do neprázdného zásobníku. Nakonec se podíváme, zda je předaná položka terminál a pokud ano, tak si tuto informaci poznačíme.
Místo kusů kódu s komentářem vytvoříme funkce s popisným názvem:
void prec_stack_push(PrecStack *s, Item item) { if (stack_is_empty(s)) { push_item_to_empty_stack(s, item); } else { push_item_to_nonempty_stack(s, item); } if (is_terminal(item)) { mark_topmost_item_as_topmost_terminal(s); } }
Zkuste si kód přečíst teď. Cítíte ten rozdíl? Nyní se kód čte tak, jakoby se jednalo o obyčejný anglicky psaný text. Nemusíme se starat o implementační detaily. Pokud by nás zajímalo, jak je něco implementované, tak se mrkneme na kód pomocných funkcí. Ty umístíme do daného modulu a označíme je jako privátní s využitím klíčového slova static
. To nám zaručí, že nedojde ke kolizi symbolů při linkování, pokud bychom v některém jiném modulu měli funkci téhož jména:
static bool stack_is_empty(PrecStack *s) { return s->top == NULL; } static void push_item_to_empty_stack(PrecStack *s, Item item) { s->top = prec_stack_create_item(item); s->top->next = s->top->prev = NULL; } static void push_item_to_nonempty_stack(PrecStack *s, Item item) { struct prec_stack_item *new_item = prec_stack_create_item(item); new_item->next = s->top; new_item->prev = NULL; s->top->prev = new_item; s->top = new_item; } static bool is_terminal(Item item) { return item.type == T_TERM; } static void mark_topmost_item_as_topmost_terminal(PrecStack *s) { s->topmost_term = s->top; }
Všimněte si, že u pomocných funkcí ani nejsou potřeba komentáře, protože z jejich popisného názvu je jasné, co dělají.
Napsání funkce místo bloku kódu s komentářem má obvykle následující výhody:
prec_stack_push()
a ujistit se, zda změnou neovlivníme kód, který je uveden později. Pokud máme kód izolovaný do samostatné funkce, stačí se podívat na parametry a návratovou hodnotu, protože to jsou jediné vstupy a výstupy.is_terminal()
by měla přijít do modulu definujícího Item
, nikoliv do modulu pracujícího se zásobníkem pro precedenční analýzu. Jedná se o zápach v kódu zvaný Feature Envy. prec_stack_push()
by nás pak nemuselo zajímat, že je odlišný postup pro vložení prvního prvku a dalších prvků.
Komentáře
_p suffix
Docela dobré je taky u funkcí, co vracejí bool, přidat do jména suffix _p, aby bylo vidět, že se jedná o predikát. Tudíž třeba is_terminal_p, etc.
Re: _p suffix
Ono
is_terminal_p()
bych četl jako "je předaný symbol terminál typup
?" Např.is_terminal_id()
("je terminál typu id"?),is_terminal_int()
("je terminál typu int?") apod. Používám raději jen prefixyhas_
,is_
apod., ze kterých je většinou patrné, o co jde.Když to vezmu trochu zeširoka, tak osobně jsem odpůrcem manglingu, maďarské notace, WinAPI konvencí apod. Přijde mi, že to naopak zbytečně zhoršuje čitelnost a správu kódu. Mám o tom již delší dobu rozepsaný příspěvek, ale ještě jsem se k jeho dokončení nedostal.
Přidat komentář