Nastal čas na kakao - Skryté podtřídy - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Soutěž

Sponzorem soutěže je:

IDIF

 

Kde se narodil známý fotograf František Drtikol?

V dnešní soutěži hrajeme o:

Seriály

Více seriálů



Začínáme s

Nastal čas na kakao - Skryté podtřídy

10. června 2004, 00.00 | Než se pustíme do popisu správného programátorského stylu pro Objective C/Cocoa a vhodných objektových vzorů, je zapotřebí se seznámit ještě s jednou koncepcí, již Cocoa často využívá, a jež významným způsobem zjednodušuje programátorské rozhraní, aniž by jakkoli omezovala flexibilitu. Jde o koncepci tzv. sdružených tříd (class clusters) a skrytých podtříd.

Než se pustíme do popisu správného programátorského stylu pro Objective C/Cocoa a vhodných objektových vzorů, je zapotřebí se seznámit ještě s jednou koncepcí, již Cocoa často využívá, a jež významným způsobem zjednodušuje programátorské rozhraní, aniž by jakkoli omezovala flexibilitu. Jde o koncepci tzv. sdružených tříd (class clusters) a skrytých podtříd.

O co vůbec jde?

Pojďme si nejprve ukázat konkrétní příklad situace, pro niž je využití skrytých podtříd zcela optimální. Dejme tomu, že chceme vytvořit třídu, jejíž instance by representovaly obecná čísla – jakkoli Objective C jako nadstavba standardního jazyka C nabízí celou jeho typovou paletu od char až po long long či long double, objektový numerický typ je nezřídka zapotřebí (a v Cocoa jej samozřejmě máme k dispozici jako NSNumber).

Problém spočívá v tom, že různých typů čísel je řada, a jakkoli mají hodně společného, zároveň se od sebe dost zásadně liší – např. takové standardní celé číslo je prostě jedno slovo ve vnitřní representaci procesoru, a to je celé; desetinné číslo typu float či double má naproti tomu poměrně složitou vnitřní strukturu mantisy a exponentu. Desetinné číslo, umožňující přesné výpočty např. cen pak musí mít ještě jinou a opět odlišnou vnitřní representaci (ve standardním C takový typ není; float či double neumožňuje obecně přesnou representaci desetinných čísel, neboť jeho vnitřní representace obvykle využívá binární soustavy, v níž jsou samozřejmě periodická jiná čísla než v desítkové; to však jsou obecné základy programování, jež do našeho seriálu vlastně nepatří, zmiňuji se o tom jen proto, že to bůhvíproč dnes málokdo ví).

V jazyce, který by neumožňoval implementaci skrytých podtříd (nebo pokud bychom se prostě rozhodli jich nevyužívat), znamená to, že musíme volit ze dvou zel:

  • Vytvoříme třídu Number, která bude sama o sobě schopna pracovat s jakýmkoli typem čísla (char, int, unsigned, long, long long, float, čísla, jež standardní C nepodporuje...). To je samozřejmě možné, ale tento přístup má řadu nepříjemných nevýhod, neboť jde o poměrně špatný, neobjektový design: naprogramování takové komplikované třídy je složité, snadno se při něm udělá chyba a složitý zdrojový kód se špatně udržuje. Druhou – a snad ještě závažnější – nevýhodou je, že implementace takové třídy není efektivní, protože musí zahrnovat potřeby všech číselných typů a nemůže být optimalizována pro potřeby jednoho konkrétního typu. Do třetice, takový kód se velmi špatně rozšiřuje: pokud bychom chtěli přidat další, nový číselný typ, a přitom udržet kompatibilitu a polymorfismus s typy již existujícími, vůbec to nebude nic jednoduchého. Proto také – alespoň pokud vím – snad žádné API tento přístup v praxi nevyužívá.
  • Namísto toho většina objektových systémů volí opačný přístup: v něm je třída Number sama pouze abstraktní nadtřídou, shrnující obecné vlastnosti všech čísel, a skutečnými reprezentanty jednotlivých typů budou její podtřídy – asi takto:

To je lepší, skutečně objektové řešení: každá z konkrétních podtříd je jednoduchá, snadno udržovatelná a snadno může být také maximálně optimalizována pro úkol, který plní. Je i snadné služby takto navrženého systému rozšířit: prostě implementujeme novou podtřídu třídy Number, a je to.

Nepříjemnou nevýhodou však je velmi komplikované programátorské rozhraní: programátor si musí pamatovat jakési třídy CharNumber, UnsignedCharNumber..., a musí se sám starat o to, aby se použila vždy právě ta nejvhodnější třída. Pokud tomu tak z nějakého důvodu není, nemusí být změna kódu – jednou přeloženého "pro třídu CharNumber" – pro spolupráci s třídou dejme tomu UnsignedInt – úplně jednoduchá. Celé je to nepohodlné, a v objektovém prostředí je to především dokonale zbytečné.

Skryté podtřídy

Cocoa pro takovéto situace využívá jednoduchoučký a přitom nesmírně efektivní trik. V zásadě nejde o nic jiného, než o lepší objektový design, odpovídající druhé variantě z minulého odstavce, kdy má každý typ čísla vlastní, jen pro sebe optimalizovanou podtřídu. Programátor ale přitom má k dispozici jednoduché, přehledné a pohodlné rozhraní definované jedinou třídou podle varianty první: využívá tedy vždy jen a pouze služeb třídy Number a její podtřídy vůbec nezná (jejich konkrétní počet a druhy dokonce vůbec nejsou součástí API, a snadno se mohou měnit mezi jednotlivými versemi systému, bez jakýchkoli záporných důsledků pro kompatibilitu programů).

Jak je to možné? Ale snadno: metody třídy Number totiž samy při vytváření objektu automaticky rozhodnou, která z jejích (skrytých) podtříd je pro dané číslo optimální a vytvoří odpovídající instanci; programátor to ale "neví" a s instancí zachází přesně stejně, jako kdyby šlo o instanci třídy Number (což je v naprostém pořádku, protože jde o dědice třídy Number). Tuto situaci ilustruje druhý obrázek – povšimněme si, že od prvého se vůbec neliší v tom, jaké obsahuje třídy, ale pouze v tom, jak jsou tyto třídy viditelné prostřednictvím programátorského rozhraní:

Takové třídě, jež je vlastně abstraktní a pouze definuje programátorské rozhraní, zatímco vlastní funkčnost je implementována na úrovni skrytých podtříd, se v Cocoa říká sdružená třída (class cluster).

V praxi to pak v programovacím jazyce vypadá tak, že vytvoříme-li několik "instancí třídy NSNumber" třeba takto:

NSNumber *aChar = [NSNumber numberWithChar:'a'];
NSNumber *anInt = [NSNumber numberWithInt:1];
NSNumber *aFloat = [NSNumber numberWithFloat:1.0];
NSNumber *aDouble = [NSNumber numberWithDouble:1.0];

může (ale nemusí) být ve skutečnosti každý z nově vytvořených objektů instancí jiné třídy. Všechny však jsou dědici třídy NSNumber a jako s takovými s nimi můžeme pracovat. Programátor nadto samozřejmě může v případě potřeby snadno sám doplnit další skryté podtřídy pro rozšíření služeb celé skupiny tříd.

Stojí za to si uvědomit, že toto skvělé řešení je v jazycích typu C++ nebo Java trochu problematické: viníkem problémů zde je špatně navržený systém tvorby objektů. Konstrukce "new Number()" jazyků Java či C++ totiž prostě nemůže vytvořit objekt jiné třídy, než právě třídy Number; skryté podtřídy zde nelze přímo použít (ovšemže se to dá do jisté míry dohnat pomocnou statickou metodou, tam však zase nastanou problémy s problémy při dědění statických metod, jež jsou bohužel dost zásadní dokonce i v jinak velmi slušně navržené Javě).

Cocoa využívá koncepce skrytých podtříd velmi často; (nejen, ale do značné míry) díky tomu je programátorské rozhraní, jež nabízí mnohem jednodušší a přehlednější, než většina ostatních API, ať je to třeba standardní skupina tříd Javy nebo např. C++kové API operačního systému Epoc: přestože služby takového Epocu nabízejí jen zlomek luxusu a flexibility služeb Cocoa.

Typickým příkladem sdružených tříd jsou prakticky všechny třídy Cocoa, které reprezentují složené objekty (jako třeba pole NSArray nebo hashované tabulky NSDictionary) – ty využívají skrytých podtříd pro volbu optimální implementace z hlediska poměru efektivity a paměťové náročnosti, aniž by se tím musel programátor explicitně zabývat. Velmi často jsou pomocí sdružených tříd implementovány i základní hodnoty – jak již zmíněná třída NSNumber, representující čísla, tak i třída NSString, jež slouží pro textové řetězce, jsou sdruženými třídami.

Pozor při tvorbě podtříd!

Sdružené třídy a jejich skryté podtřídy jsou skvělý trik, který významným způsobem usnadňuje programování v Cocoa aniž by jakkoli omezil flexibilitu a nabídku služeb; jednu nevýhodu ale má: musíme si dávat pozor, chceme-li vytvořit novou podtřídu některé ze sdružených tříd.

U běžných tříd vytváříme podtřídy tehdy, chceme-li nějak rozšířit jejich služby. Z principu sdružených tříd je zřejmé, že u nich tomu tak není! Novou podtřídu sdružené třídy vytváříme tehdy, když chceme zajistit podporu nového typu dat při zachování původní skupiny služeb. Konkrétně,

  • pokud by Number byla běžná třída, její podtřídu bychom mohli připravovat třeba pro čísla se jmény: dědila by schopnost representovat jakékoli číslo, a my bychom k tomu přidali ještě pojmenování. Naproti tomu, pokud bychom chtěli přidat nový typ čísla s týmiž službami, jaké nabízí Number – třeba dekadické číslo libovolné délky, interně representované textovým řetězcem, obsahujícím jeho desítkový zápis –, nijak přímo by to nešlo: museli bychom vytvořit novou třídu, jež by nebyla dědicem Number, ale zpracovávala by stejné zprávy;
  • pokud však je Number sdružená třída, budeme připravovat její podtřídu právě pro novou vnitřní representaci (např. pro ono dekadické číslo libovolné délky s textovou representací), jež bude nabízet přesně tytéž služby, jako všechna ostatní čísla. Naopak pro pojmenované číslo nemůžeme použít podtřídu, ale vkládání – vytvoříme novou třídu (jež může a nemusí být dědicem Number), jež však obsahuje objekt třídy Number. To proto, že podtřídy od třídy Number nedědí schopnost representovat číslo; dědí jen programátorské rozhraní, avšak schopnost representace musí doplnit samy.

V praxi to znamená, že začínající programátoři obvykle podtřídy sdružených tříd nevytvářejí (protože jen velmi zřídka je zapotřebí přidat nový způsob representace pro některou z nich). Namísto toho využívají právě vkládání objektů; my se mu budeme věnovat jako samostatné technologii v některém z příštích dílů našeho seriálu.

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Začínáme s  

 

 

 

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: