JE_29 - Minimalizujte obor platnosti lokálních proměnných

Tato rada se svou podstatou podobá radě 12 označené "Minimalizujte přístupnost třída a členů". Minimalizováním obotu platnosti lokálních proměnných zvyšujete čitelnost a spravovatelnost svého kódu a snižujete prvděpodobnost vzniku chyby.

Programovací jazyk C vyžaduje, aby byly lokální proměnné deklarovány v záhlaví bloku, a programátoři v tom ze zvyku pokračují; je to zvyk který stojí za překonání. Jen pro připomenutí: Programovací jazyk Java vám umož�uje deklarovat proměnné, kdekoli je platný nějaký výraz.

Nejsilnější technikou minimalizování oboru platnosti (rozsahu) lokální proměnné je deklarovat ji tam, kde je poprvé použita. Je-li nějaká proměnná deklraována ještě než je použita, jen se tím zvyšuje nepřehlednost kódu a zbytečně to mate čtenáře, který se snaží pochopit, co program dělá. V okamžiku, kdy je taková proměnná použita, si už čtenář nemusí pamatovat její typ nebo počáteční hodnotu. Jestliže se program vyvíjí a taková proměnná se už nepoužívá, snadno lze zapomenout na odstranění její deklarace, pokud se nachází daleko od míst prvního použití.

Nejenže může předčasné deklarování lokální proměnné způsobovat rozšíření jejího oboru platnosti příliš brzy, ale také příliš pozdě. Obor lokální proměnné se rozšiřuje od bodu její deklarace až na konec obklopujícího bloku. Je-li nějaká proměnná deklarována mimo blok, v němž je použita,zůstává viditelnou i v okamžiku, kdy program opustí daný blok. Je-li nějaká proměnná náhodou použita před nebo za oblastí svého zamýšleného použití, mohou být následky katastrofální.

Téměř každá deklarace lokální proměnné by měla obsahovat inicializátor. Nemáte-li ještě dostatek informací k rozumnému inicializování nějaké proměnné, pak byste měli deklaraci odložit na později. Výjimkou z tohoto pravidla jsou příkazy try - catch. Je-li nějaká proměnná inicializována metodou vyvolávající určitou nekontrolovanou výjimku, musí být inicializována uvnitř bloku try. Musí-li být daná hodnota použita vně bloku try, pak musí být deklarovaná před blokem try, kde ještě nemůže být "rozumně inicializována". Příklad najdete v radě radě 35.

Smyčky představují speciální příležitost k minimalizování oboru platnosti proměnných. Smyčka for vám umož�uje deklraovat proměnné smyčky a omezit tak jejich rozsah na přesnou oblast, kde jsou zapotřebí. tento region se skládá z těla smyčky i inicializace, testu a aktualizace před tělem. Proto dávejte přednost smyčkám for před smyčkami while, pokud tedy není obsah proměnnýh smyčky zapotřebí i po ukončení smyčky.

Zde máme například upřednost�ovaný princip iterování kolekcí:

for (Iterator i = c.iterator(); i.hasNext();) {
	doSomething(i.next());
}

Abychom si ukáazli, proč je tato smyčka lepší než zjevnější smyčka while, zvažme následující ukázku kódu, který obsahuje dvě smyčky while a jednu chybu:

Iterator i = c.iterator();
while (i.hasNext()) {
	doSomething(i.next());
}

	. . . 

Iterator i2 = c2.iterator();
while (i.hasNext()) { // CHYBA!
	doSomethingElse(i2.next());
}

Druhá smyčka obsahuje chybu vyjmutí a vložení: Inicializuje novou proměnnou smyčky i2, ale používá tu starou i, která je bohužel stále v oboru platnosti. Výsledný kód se přeloží bez chyby a spustí n¨bez vyvolání výjimky, ale funguje nesprávně. Místo iterování c2 skončí druhá smyčka okamžitě, čímž vznikne nepsrávný dojem, že c2 je prázdné. Protože tento program bude chybovat potichu, může chyba zůstat neodhalená dlouhou dobu.

Pokud by se stejná chyba po vyjmutí a vložení objevila v preferovaném idiomu smyčky for, výsledný kód by se ani nezkompiloval. Proměnná první smyčky by nebyla v oboru platnosti v místě, kde se nachází druhá smyčka:

for (Iterator i = c.iterator(); i.hasnext();) {
	doSomething(i.next());
}

// Chyba při kompilaci - symbol i nelze rozpoznat
for (Iterator i2 = c2.iterator(); i.hasnext();) {
	doSomething(i2.next());
}

Když použijete princip smyčky for, navíc méně pravděpodobné, že se dopustíte chyby vyjmutí a vložení, protže není zapotřebí používat v obou smyčkách jiný název proměnné. Smyčky jsou naprosto nezávislé, takže není na škodu znovu použít stejný název proměnné smyčky. Je to vlastně dokonce stylové.

Idiom smyčky for má ještě jednu výhodu oproti principu smyčky while, třebaže je menší. Idiom smyčky for je o řádek kratší, což umož�uje vtěsnání obklopující metody do okna editoru pevné velikosti, čímž se zvyšuje čitelnost.

ZDe máme další princip smyčky sloužící k iterování seznamem, který minimalizuje obor platnosti lokálních proměnných:

// Velmi výkonný princip iterování seznamy s náhodným přístupem
for (int i = 0, n = list.size(); i < n; i++)
	doSomething(list.get(i));

Tento princip je užitečný u implementací List s náhodným přístupem, jako je ArrayList a Vector, protože bude pravděpodobně u takových seznamů vykonán rychleji než výše uvedený "upřednost�ovaný idiom". Důležité je všimnout si na tomto principu toho, že má dvě proměnné smyčky, i a n, přičemž obě mají přesně ten správný obor platnosti. Použití druhé proměnné je pro výkonnost tohot idiomu zásadní. Bez ní by musela smyčka volat metodu size jednou v každé ieraci, což by znehodnotilo výkonnostní výhodu získanou tímto principem. Použití tohot idiomu je přijatelné, když jste si jistí, že daný seznam skutečně poskytuje náhodný přístup; jinak je patrná kvadratická výkonnost.

Podobné idiomy existují i pro další smyčky, například:

for (int i = 0, n = expensiveComputation(); i < n; i++)
	doSomething(i);

Tento idiom znovu používá dvě proměnné smyčky, přičemž ta druhá nazvaná n se používá k omezení nákladů na vykonávání nadbytečného výpočtu při každém průchodu. Obecně byste měli používat tento princip, pokud test smyčky zahrnuje volání nějaké metody a toto volání zaručeně vrátí stejný výsledek při všech průchodech.

Poslední techniku minimalizování oboru platnosti (rozsahu) lokálních proměnných je udržovat metody malé a zacílené. Zkombinujete-li dvě činnosti v jedné metodě, mohou se lokální proměnné, které se týkají jedné činnosti, objevit v oboru platnosti kódu vykonávajícího druho činnost. Chcete-li tomu zabránit, prostě rozdělte danou metodu na dvě, jednu pro každou činnost.