Vše co jste chtěli vědět o složených závorkách, ale báli jste se zeptat

Stejně jako každý ninja musí prokázat, že dokáže zabít i roličkou toaletního papíru, i já předvedu stupeň své nezměrné zvrhlosti tím, jak dokážu používat složené závorky. Složené závorky asi používáte všichni, ale všichni před ně určitě amatérsky píšete něco jako jméno třídy, metody, if, for nebo něco podobného. To já už jsem dosáhl vyšší úrovně zasvěcení a nepíšu před ně vůbec nic. Předem vás chci upozornit ať to doma radši nezkoušíte, předvedená technika vyžaduje léta tréninku, dřiny a odříkání.

První možností, kde se se závorkou bez ničeho můžete setkat, je inicializační blok. Vypadá to následovně.

public class UglyClass {
	{
		System.out.println("Ugly code");
	}
	{
		System.out.println(" rocks :-/");
	}
}

Krása, že? Ano, opravdu tento kód je v Javě přípustný. Tyto bloky jsou zavolány pokaždé, když je třída instancována a to v pořadí, v jakém jsou uvedeny v kódu. Výhodou má být, že je tento kód sdílený mezi všemi konstruktory. Jenom tiše doufám, že to nikdo nepoužívá. Byla by to opravdu krásná past, schovat podobný blok někam na konec třídy a tam něco ošklivého provést. Dlouho jsem přemýšlel, jestli by existovalo nějaké rozumnější využití. Nebyl by to můj ohavně zvrhlý mozek, kdyby něco nevymyslel. Tady to máte:

		List<Address> addresses = Arrays.asList(
			new Address()
			{
				{
					setStreet("Ugly 13");
					setCity("Brno");
					setZip("000000");
				}
			},
			new Address()
			{
				{
					setStreet("Ugly 15");
					setCity("Brno");
					setZip("000000");
				}
			}
		);

Představte si, že chci nainicalizovat seznam adres (třeba v unit testu) a nemám vhodný konstruktor. Tak si vytvořím anonymního potomka a v jeho inicializačním bloku si nastavím co potřebuji. Cool.

Samozřejmě normální, konzervativní varianta je takováto:

		List<Address> addresses = new ArrayList<Address>(2);
		Address address1 = new Address();
		address1.setStreet("Ugly 13");
		address1.setCity("Brno");
		address1.setZip("000000");
		addresses.add(address1);

		Address address2 = new Address();
		address2.setStreet("Ugly 15");
		address2.setCity("Brno");
		address2.setZip("000000");
		addresses.add(address1);

Nuda co? Navíc má tato varianta jeden velký problém. Často v ní udělám chybu. Jako třeba v uvedeném příkladě. Kdo si té chyby všiml rovnou, má bod, já bych ji tam hodně dlouho hledal.

Abych podobné chybě zabránil, mohu použít druhou možnost, jak napsat osamocené složené závorky. Prostě je fouknu doprostřed metody, čímž vytvořím normální blok kódu.

		List<Address> addresses = new ArrayList<Address>(2);
		{
			Address address1 = new Address();
			address1.setStreet("Ugly 13");
			address1.setCity("Brno");
			address1.setZip("000000");
			addresses.add(address1);
		}
		{
			Address address2 = new Address();
			address2.setStreet("Ugly 15");
			address2.setCity("Brno");
			address2.setZip("000000");
			addresses.add(address2);
		}

Dosáhnu tím toho, že omezím platnost lokálních proměnných. Překladač mě pak nenechá do seznamu vložit stejnou adresu dvakrát. To už je použití, které by se možná i dalo obhájit.

A teď si představte, že člověku jako já, dáte do ruky closures (uzávěry). To by pak museli čtenáře mého kódu rovnou uzavřít do blázince. Aha, už alespoň vím, proč se closures jmenují, tak jak se jmenují.

Pokud máte opravdu silné nervy, podívejte se na tento článek, tam uvidíte, že já jsem v podstatě neškodný.

Proč jsme schizofrenní

Úlohy se dají řešit buď dobře nebo rychle. Vy je musíte řešit dobře a rychle.
Doc. RNDr. Zdeněk Kluiber, Csc.,PhD.

Dagi mi svým posledním článkem nahrál na příspěvek, který jsem chtěl už napsat dlouho. Je o jisté schizofrennosti práce programátorů. Programátoři musí stále plnit protichůdné požadavky. Máme psát jednoduchý znovupoužitelný kód. Máme psát kód, který je čistě napsaný a přitom výkonný. Máme programovat dobře a přitom rychle. Všechno jsou to požadavky, které jdou proti sobě. Kód může být buď jednoduchý nebo znovupoužitelný. Může být snadno pochopitelný nebo výkonný.

Obvykle samosebou volíme kompromis. Píšeme kód, který je více méně jednoduchý a v rámci možností znovupoužitelný. Ale ta touha po dokonalosti tu stále je. Často se přistihnu při myšlence, že kdybych měl dost času, zkušeností a talentu, tak bych tyto protichůdné požadavky skloubil dohromady. To je samozřejmě nesmysl. Čím dál tím více zjišťuji, že hlavní náplní mé práce je hledání kompromisů. Musím zvolit návrh v závislosti na požadavcích. Pokud jsou velké nároky na výkon, musí jít stranou dokonalost návrhu. Pokud je nejdůležitějším požadavkem konfigurovatelnost, musí jít stranou jednoduchost.

Uvedu to na jednoduchém příkladu (není moc původní, přivedl mě na něj kolega). Máme vymyslet formát souboru, pro přenos informací o zákaznících. Aby to nebylo tak jednoduché, tak každý zákazník může mít několik adres a několik telefonních čísel. První co mě napadne, je jednoduchý XML formát, kde bude vždy údaj o zákazníkovi a v něm budou vnořeny seznamy adres a telefonních čísel. To by byl čistý, snadno znovupoužitelný formát. Všechno se ale změní, když zjistím, že těch zákazníků mám milión a tento soubor budu generovat na základě dat v databázi. Potom mě tento návrh donutí udělat přes milión dotazů do databáze, pokaždé když budu chtít vyexportovat data. (Za předpokladu, že se mi to všechno nevejde do paměti).

Za těchto okolností se asi budu více přiklánět k tomu, že nejdříve vygeneruji seznam zákazníků, potom seznamy adres a telefonních čísel a spojím je pomocí nějakého klíče. To mi zmenší počet nutných dotazů na tři. Jenom ten formát bude mnohem méně elegantní a rozšířitelný. Bude prostě víc relační než objektový.

V tomto příkladě mě ani nenapadá žádné kompromisní řešení. Buď kladu větší důraz na výkonnost nebo na čistotu návrhu i když vnitřně tak nějak toužím po obojím.

Tato schizofrenie se potom projevuje i na našich požadavcích. Chceme programovací jazyk, který nám dá svobodu, ale zároveň nám bude co nejvíce pomáhat a kontrolovat nás. Chceme technologie, které jsou jednoduché a zároveň obecné a rozšířitelné. Obvykle si neuvědomujeme, že to co chceme, nemůže existovat. Podle mě je při výběru technologie důležitá možnost výběru podle požadavků. Pokud vím, že chci dělat něco jednoduchého, tak chci mít možnost to udělat jednoduše. Pokud vím, že chci dělat něco specifického, chci aby mi to daná technologie umožnila. Chci dát své schizofrenii prostor. Pokud jsem nucen dělat jednoduché věci složitě nebo se nemohu odchýlit od předem vytyčeného směru tak přestávám být schizofrenní, začínám být maniodepresivní.

Tomcat killer demystified

This entry contains solution of the mystery mentioned in the previous one.

Big Detective: Dear Watson, do you know who is the killer?
Dr. Watson: No, I have no idea, it is so mysterious. I think, there has to be some bug in the JVM.
Big Detective: Interesting hypothesis. But not plausible.
Dr. Watson: But as you say “Eliminate all other factors, and the one which remains must be the truth.” I do not see any other solution.
Big Detective: We will see. Let’s arrange all the facts. First of all, the virtual server has only 256 megabytes of memory. What happens when it is not enough?
Dr. Watson: Well, the swap space is used.
Big Detective: Usually yes, but not in our case. Unfortunately the configuration of the virtual server does not enable swap.
Dr. Watson: I see, but what happens when the server does not have enough memory?
Big Detective: That’s a good question. Linux has something called OOM killer. As you can read in Who is Who: “It is the job of the linux ‘oom killer’ to sacrifice one or more processes in order to free up memory for the system when all else fails.”
Dr Watson: So OOM killer kills Tomcat when Linux does not have enough memory! But it means that adding more heap to the Tomcat JVM makes the trouble even worse!
Big Detective: Exactly,Watson.