Skládání objektů a dynamické rozpoznání zpráv

V pátém příkladu se seznámíme s komplikovanějším mechanismem, který v Objective C umožňuje dynamické rozpoznávání zpráv: při implementaci třídy můžeme sami určit kód, který bude přidělovat každé zprávě metodu, která ji má zpracovat. Zároveň si ukážeme další mechanismus, který Objective C nabízí: jedná se o přesměrování zpráv -- objekt si může zpracování zprávy vyžádat od jiného objektu.

Příklad bude odpovídat vícenásobné dědičnosti, známé z jiných jazyků. Vytvoříme novou třídu, která bude 'dědit' po skupině tříd Class_1 až Class_N; toto dědění zajistíme tak, že objekt naší třídy si vytvoří objekty všech 'vnořených' tříd a dostane-li nějakou zprávu, předá ji ke zpracování některému z těchto vložených objektů.

// Objective C -- příklad 5
//
// ukázka skládání objektů a dynamického rozpoznávání zpráv
// pro simulaci vícenásobné dědičnosti
// (tento příklad není úplným programem)

// importujeme interface všech tříd, po kterých chceme dědit
#import "Class_1.h"
...
#import "Class_N.h"

#define NCLASSES N // počet děděných tříd (pro jednoduchost statický)

@interface MultipleInheritance: Object
{
id class[NCLASSES]; // pole vložených objektů

}
// předpokládejme, že nová třída nemá žádné 'vlastní' metody, pouze ty zděděné
@end

@implementation MultipleInheritance
// musíme reimplementovat metodu init pro inicializaci vložených objektů
-init
{
class[0]=[Class_1 new];
...
class[N-1]=[Class_N new];
return self;
}
// analogicky musíme reimplementovat metodu free
-free
{
int i;

// zde můžeme snadno určit pořadí uvolňování objektů
for (i=0;i<NCLASSES;i++)
[class[i] free];
return [self free];
}
-forward:(SEL)msg :(marg_list)parms
{
int i;

for (i=0;i<NCLASSES;i++)
// je-li vložený objekt schopen zprávu zpracovat ...
if ([class[i] respondsTo:msg])
// ... předáme mu ji!
return [class[i] performv:msg:parms];
// žádný z vložených objektů zprávu nemůže zpracovat ...
// mohli bychom třeba ohlásit chybu, nebo zprávu ignorovat:
return self;
}
@end
// end of file

1. Interface

Properties objektu obsahují pole odkazů na vnořené objekty. Stejně jako v případě skutečné vícenásobné dědičnosti je zde pole statické; povšimněme si však, že bychom mohli snadno využít dynamické pole, alokované při inicializaci objektu -- tedy něco, co již vícenásobná dědičnost není schopna zajistit.

Nedefinujeme žádné metody, protože objekt žádné vlastní metody neimplementuje: reimplementované metody 'init' a 'free' dědí po třídě Object a všechny ostatní metody přesměruje na vložené objekty.

2. Inicializace

V rámci metody 'init' prostě vytvoříme všechny vložené objekty a jejich identifikace uložíme do pole 'class'. Uvědomme si, že můžeme libovolně změnit pořadí vytváření (a tedy i inicializace) jednotlivých vložených objektů a že můžeme při vytváření kteréhokoli z nich určit jakékoli potřebné parametry -- i to bývá při využití klasické vícenásobné dědičnosti často problém.

3. Uvolnění objektu

Podobně jako metodu 'init' musíme reimplementovat i metodu 'free' -- ta nejprve uvolní všechny vložené objekty a teprve nakonec voláním '[self free]' objekt uvolní sám sebe. Opět můžeme snadno změnit pořadí uvolňování jednotlivých objektů a/nebo můžeme před uvolněním objektu ještě provést jakoukoli jeho potřebnou 'deinicializaci' (uložení dat do souboru apod.).

4. Dynamické rozpoznání zpráv

Pro dynamické rozpoznání zpráv nabízí Objective C zprávu 'forward::'. Ta funguje jednoduchým způsobem: dostane-li kterýkoli objekt zprávu, kterou není schopen zpracovat, pošle mu runtime systém Objective C automaticky zprávu 'forward::'; v jejím rámci může objekt zprávu zpracovat dynamicky. Zpráva 'forward::' má dva parametry: prvním z nich je selektor předávané zprávy, druhým z nich je ukazatel na její parametry.

Implementace zprávy 'forward::' je v našem případě velmi jednoduchá: postupně se 'optáme' všech vloženým objektů jsou-li schopny zprávu zpracovat -- využijeme k tomu standardní zprávu 'respondsTo:', se kterou jsme se seznámili již v minulém příkladu. Jakmile narazíme na objekt který zprávu zpracovat umí, přesměrujeme ji.

Je zřejmé, že bychom mohli algoritmus vyhledávání objektu, kteýr bude zprávu zpracovávat, libovolně obměňovat: mohli bychom měnit pořadí zkoumaných objektů, mohli bychom např. vždy preferovat objekt, který zpracoval minulou zprávu a podobně.

Poznámka: v OpenStepu jsou možnosti dynamického zpracování zpráv výrazně rozšířeny díky třídě NSInvocation -- viz odpovídající článek ze série o FoundationKitu.

5. Přesměrování zpráv

Pro přesměrování zpráv slouží standardní zpráva 'performv::'. Jejími parametry je selektor zprávy a seznam parametrů, v přesně stejném formátu v jakém je předává zpráva 'forward::'.



(obsah)


Copyright (c) Ondra Čada