Dokumentační komentáře funkcí

Školení, která pořádám

Pro dokumentaci funkcí – jejich chování, parametrů a návratové hodnoty – je zvykem používat dokumentační komentáře:

<?php
/** Vrátí podřetězec v kódování UTF-8
* @param string
* @param int pozice počátečního znaku počítaná od nuly, záporné hodnoty: od konce
* @param int délka ve znacích, záporné hodnoty: pozice od konce
* @return string
*/
function utf8_substr($string, $start, $length = null) {
}
?>

Tento způsob je velice rozšířený, většina vývojářů ho dokáže dobře číst i psát, podporují ho nástroje pro generování dokumentace i vývojová prostředí. Mně ale příliš nevyhovuje, protože parametr a jeho dokumentace jsou na různých místech, i když poměrně blízko. To vede k tomu, že se při přidávání parametru dá na jeho dokumentaci snadno zapomenout nebo se špatně umístí a dokumentace pak s kódem nesouhlasí. Někdo se tomu snaží zabránit zopakováním názvu parametru i v komentáři (/** @param int $start */), to ale vede k další duplicitě a možnému vzniku nekonzistence. V JavaScriptu (kde se do komentářů musí psát i výchozí hodnota parametrů) je situace ještě horší.

Alternativa

Jaký způsob zápisu by tyto nedostatky odstranil?

<?php
function utf8_substr( /// Vrátí podřetězec v kódování UTF-8
    $string, /// @param string
    $start, /// @param int pozice počátečního znaku počítaná od nuly, záporné hodnoty: od konce
    $length = null /// @param int délka ve znacích, záporné hodnoty: pozice od konce
) { /// @return string
}
?>

Tento zápis mi ale přijde tak nepřehledný, že i když by mohl být méně náchylný k nekonzistencím, používat bych ho asi nechtěl. Trochu by mu mohlo pomoci zarovnání komentářů do stejného sloupce (což je činnost, kterou nesnáším), ale nijak výrazně.

Jakub Vrána, Seznámení s oblastí, 29.6.2011, diskuse: 24 (nové: 0)

Diskuse

Jakub Onderka:

Řešením může být použít IDE, které chybné nebo neúplné dokumentační komentáře dokáže odhalit – například PHPStorm.

Naith:

Jakube, druhá varianta vypadá špatně i na jediné funkci. Cvičně jsem takto zformátoval jednu třídu... je to katastrofa. Ta nepřehlednost je brutální. Nechtěl bych takto dostat neznámý zdroják.

ikona Martin Štekl:

Určitě to je o zvyku. Mě například vyhovuje první způsob především proto, že výrazně vizuálně odlišuje jednotlivé funkce. Pokud bych si u druhého formátu hrál s tím, že před každou funkci dám řádek rovnítek, pomlček nebo čehokoli jiného v komentáři, bylo by to jistě lepší. Ale i tak by mi první způsob přišel přehlednější.

ikona Martin Štekl:

Sám používám první způsob s duplicitním vypsáním názvu proměnné. Používám Netbeans IDE a v při refaktoringu se umí změnit i názvy proměnných v komentářích, tedy to ničemu nevadí. Základ formátu pak IDE samo umí vygenerovat.

ikona kukulich:

Název parametrů do komentáře patří, protože pořadí @anotací nemusí být popořadě. Nepsat to tam kvůli možnému vzniku nekonzistence je nesmysl, protože nekonzistence může vzniknout i třeba u typu parametru. Prostě pokud měním hlavičku metody a nezměním dokumentační komentář, tak jsem udělal něco špatně.

Mimochodem, náš ApiGen při použití konfigurace --undocumented=file.log hlásí právě i špatně zdokumentované parametry. Pokud to neumí IDE, dá se dokumentace tedy snadno kontrolovat třeba při jejím generování.

ikona Jakub Vrána OpenID:

Název parametrů do komentáře nepatří, protože už je uveden v deklaraci funkce. Je tedy zbytečné ho opakovat. Anotace @param by popořadě být rozhodně měly.

ikona kukulich:

A typy "array", "stdClass" atd. také do dokumentace nepatří, když už jsou v deklaraci funkce?

ikona Jakub Vrána OpenID:

Kdyby se takhle daly určit všechny typy, tak ne. To ale nejdou, takže konzistence vítězí před sebeopakováním.

Obdobné je to s výchozí hodnotou – ta se dá určit v deklaraci, takže opakovat ji v dokumentaci je opět zbytečné. Zvláštním případem jsou výchozí hodnoty, které se v deklaraci určit nedají (např. new stdClass nebo time()) a které se obvykle obchází pomocí předání NULL. Tam ale v dokumentaci nepopisuji, co je výchozí hodnota, ale co znamená předaný NULL.

juzna.cz:

Me se zase libi mit v anotacich i jmena argumentu, protoze pak vidim vsecko prehledne na jednom radku - typ + jmeno + komentar. Kdyz je argumentu vice, tak to vypada skoro jako tabulka ve ktere se mi pekne orientuje.

filips:

Správnost názvů a pořadí parametrů lze kontrolovat PHP_CodeSnifferem.

cooler:

Používám první variantu, používám IDE Zend Studio (eclipse). Je to docela funkční...

Jak se vyrovnáváte s tím, když např. fce vrací array nějakých objektů? Jaké jsou možnosti zápisu? Jak to které IDE zvládne pochopit? Nebo používáte třídu zastřešující array? (mam na mysli napr. "Class RowSet", která v podstatě drží "array of Row classes")

ikona The Zero:

Nejnovější Eclipse (Indigo) už právě @return array of RowClass chápou.

Denis:

Já komentuju jen ty parametry, kde by nemuselo být jasné, co dělají a jen ty funkce, u kterách nemusí být jasné, co dělají.

Rozhodně nikdy nepíšu samoúčelné věci typu @param string.

Paramter by měl prozrazovat k čemu je, již svým jménem. Stejně tak by to měl dělat název funkce.

Funkce by se dále neměla pokoušet uvažovat za uživatele nebo dělat nějakou práci mimo svůj název a parametry. Jako to dělá třeba funkce count(), kde count(false) vrátí 1, což nám říká, že pole nebo objekt false obsahuje jeden prvek byť false není ani objekt ani pole :)

Pokud se ale spojí netypový jazyk spolu s funkcemi, které se chovají nepředvídatelně, potom je opravdu nezbytné komentovat vše. Bohužel.

ikona Jakub Vrána OpenID:

Jak postupuješ, když si komentář zaslouží třeba jen třetí parametr? Uveď prosím ukázku kódu.

Denis:

Vždy vypisuji i název parametru.

Absence komentáře u srozumitelných parametrů (nebo funkcí) dává pocit jistoty, že nemusíte nikde nic studovat, jelikož funkce se chová tak, jak se očekává. Resp. by u těchto funkcí mohl být jen komentář, že "funkce dělá to, co se očekává", aby bylo jasné, že se na komentář nezapomnělo.

Samozřejmě, že pokud se funkce jmenuje strstr() a její parametry jsou "kupka sena" a "jehla", pak nastává problém :D :D

ikona Jakub Vrána OpenID:

Mohl bys prosím uvést tu ukázku kódu? Třeba na příkladu funkce utf8_substr().

Denis:

<?php

function utf8_substr($string, $start, $length = null) {
/**
* @param int $start pozice počátečního znaku počítaná od nuly, záporné hodnoty: od konce
* @param int $length délka ve znacích, záporné hodnoty: pozice od konce
*/

}
?>

Takto nějak bych si to představoval, ale nevím, jak to sežere phpDocumentor :D. Já v PHP nepoužívám žádný dokumentační systém, komentáře píšu podle sebe a tím pádem si ani negeneruju žádnou dokumentaci.

ikona Miloslav Ponkrác:

On je také problém, že v dynamických jazycích je možné mít více kombinací parametrů. V PHP u konstruktorů tříd a někdy u metod používám možnost, že je možné několik schémat (tedy různých typů a počtu parametrů).

Pak konstruktor je definován jako funkce bez parametrů. Parametry si následně vytáhnu pomocí func_get_args() a zpracuji podle svého.

A dostat to do dokumentačního systému jsem vzdal. V komentáři prostě napíši ručně všechny možnosti, pro které je to děláno.

Jinak také si myslím, že nejlepší dokumentace je rozumné a intuitivní pojmenování všech identifikátorů. Nic lepšího a rychlejšího pro orientaci neexistuje.

Běžně píšu zdrojové kódy tak, že není třeba žádný komentář a cizí osoba se velmi snadno zorientuje. Protože indentikátory nají intuitivně zvolené názvy.

Komentáře pomocí dokumentačního systému pak spíše zdroják znepřehledňují, protože kód je roztažen při editaci na více obrazek (chci-li ho vidět celý) – takže přehodnocuji strategii komentování pro dokumentační systém. On se komentář dá udělat i do zvláštního souboru (doxygen s tím nemá problém), pokud nechci aby strašil a přidával vatu do zdrojáku, který chci čistě vidět.

Ale PHP má obecně problém, že jeho dynamické konstrukce jsou mocnější, než konvenční dokumentory dokáží zvládnout.

Na druhé straně udělat si vlastní dokumentor pro PHP je směšně snadné. Součástí PHP je parser PHP kódu a extrahování značek z komentářů je snadné.

A kromě toho se dá i integrovat do doxygenu. Doxygen umožňuje programovat pluginy, pár jsem jich v C++ napsal a rozšířil tak doxygen tam, kde původně neumí – třeba pro zařazení assemblerovského kódu a modulů a stejně tak se dá rozšířit i pro další PHP věci.

Ale jak říkám, nejlepší kód je takový, kde intuitivně poznáte co dělá bez komentářů. Pokud začnete trikovat a machrovat, jak dokážete napsat jednoduchý kód co nejsložitěji – pak to nejde. Stejně je to problém funkcionálních programovacích jazyků – třeba Haskellu, nebo i běžného LISPu – jsou to mocné jazyky, ale musíte být pořád ve střehu a nejde spoustu kódu číst bez toho abyste byli tvrdě ve střehu a i běžný kód chce velkou pozornost.

PHP ale lze snadno psát tak, aby druhý člověk intuitivně věděl co dělá zcela be zkomentářů, nebo jen sem tam s drobným komnetářem. Není problém.

juzna.cz:

Bez komentaru muzes poznat co kod dela, ale nemusis pochytit smysl/ucel, nebo okrajove/netypicke moznosti. To same plati pro argumenty funkci. Pokud ma s kodem pracovat vice lidi, tak je dobre psat komentare vsude, clovek pak nemusi studovat kod a jednoduse si precte co potrebuje vedet.

Denis:

Toto platí jen v případě, že programátor nezapomene dokumentační komentáře při změně funkce upravit.

Což se může dít velmi často, protože při větších úpravách se upravuje hodně částí kódu najednou a poslední věc, co programátor v tu chvíli chce dělat, je hlídat dokumentační komentáře.

Já místy používám dvě věrze funkcí. např.

<?php utf8_substr($string, $start, $length = null)?> a <?php utf8_substr_ex($string, $start, $dalsiParametr1, $dalsiParametr2, $length = null)?> jako extended, kde původní funkce dělá přesně to, jak se jemnuje bez čehokoli nečekaného a nepředvídatelného. Extended funkce již může obsahovat rozšířenou funkčnost, takže zde se hodí přidat komentář. Samozřejmě toto nelze použít vždy, ukazuji to jen jako další možnost.

ikona Miloslav Ponkrác:

„Bez komentaru muzes poznat co kod dela, ale nemusis pochytit smysl/ucel, nebo okrajove/netypicke moznosti.“

A tomu se říká čistota kódu, aby netypických možností bylo co nejméně.

Já jsem ze staré školy. Dnešní mladí programátoři se předhánějí v trikování, v tom jak udělají trikovější zápis toho a onoho – a v 99,999% jim to kromě nesrozumitelnosti a namachrovanosti když o tom píší na webu nic jiného nepřinese.

A pokud funkce dělá něco, co nejde intuitivně poznat, tak se samozřejmě napíše komentář.

„To same plati pro argumenty funkci. Pokud ma s kodem pracovat vice lidi, tak je dobre psat komentare vsude, clovek pak nemusi studovat kod a jednoduse si precte co potrebuje vedet.“

Vy jste oběť dnešní doby. Kvantita neznamená kvalitu.

Rozsáhlá emise rozvětvených slov a komentářů neznamená, že to bude srozumitelnější.

Měl jsem vždycky problémy s tím, že můj kód býval druhým bezvadně srozumitelný – a to i s minimem komentářů. Je to prostě na něm jasně vidět co kde se co dělá.

Navíc další zamlžení – to o čem mluví tento článek a o čem diskutuje není de facto dokumentace, ale referenční příručka vygenerovaná nějakým automatickým nástrojem stylu javadoc. Jako taková má všechny ctnosti i nectnosti automatické dokumentace – není to příručka prvního typu, kteoru někomu dáte při prvním seznamování, respektive není na to není optimální.

Je úplně jedno, kolik napíšete komentářů, protože cílem komentářů není jejich množství, ale cílem komentářů je něco zcela jiného – objasnit druhému co dělá Váš kód.

Pokud napíšete 2 komentáře na 100 000 řádek kódu a vše je perfektně všem srozumitelné, protože je dobře volená struktura i architektura programu, intuitivní identifikátory a další věci, pak komentáře splnily účel.

Pokud zdroják ozdobíte tunami komentáře, se kterými mi vše bude nejasné a budu potřebovat dva doktoráty z atomové fyziky a 3 měsíce času abych pochopil, co se v kódu děje, pak jste sice formálně dodržel pravidlo komentování, ale je to úplně na pendrek. Kdybyste nekomentoval, bylo by to úplně stejné. Ale ano, kryl jste si záda, dokázal jste Vašemu nadřízenému, že řádně plníte jeho nařízení o počtu komentářů – a dělal jste zcela zbytečnou věc.

Pravidla jsou hezká věc, ale cílem není dodržování pravidel (jak vidíte na tom, jak třeba funguje EU, která jde touto cestou), ale cílem je splnit srozumitelnost kódu.

ikona Miloslav Ponkrác:

„Samozřejmě, že pokud se funkce jmenuje strstr() a její parametry jsou "kupka sena" a "jehla", pak nastává problém“

Jaký problém? Přejmenování názvů parametrů je v cuku letu.

Žádný program používající funkci strstr nebude ovlivněn, protože formální jméno parametrů funkce nijak neovlivňuje v PHP nic, co funkci používá. Na rozdíl třeba od Pythonu, kde je možné funkce volat s použitím jmen parametrů – ale v PHP se nic takového neděje.

Doporučoval bych nehledat problémy kde nejsou.

Kdyby tým PHP chtěl, tak tuhle věc přejmenuje během pár minut.

Nehledě na to, že hledání jehly v kupce sena určitou mnemotechničnost má.

ikona Miloslav Ponkrác:

A poznámku, s doxygenem na žádný parametr nezapomenete. Stejně tak nepřidáte, který není.

V logu při vytváření dokumentace je seznam všech nesrovnalostí komentářů a skutešných funkcí/metod, proměnných, atd.

Jirka:

Díky za článek, zajímavé čtení, ale jak tu již někdo napsal nehledal bych problém tak kde vlastně ani není :-)

Když pracujete na nějaké větší aplikací s více lidmi oceníte jakýkoliv komentář, který najdete xD vlastní zkušenost..

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.