Dynamické rozpoznání třídy
Čtvrtým příkladem není kompletní program, ale pouze doplněk k minulému příkladu. Uvědomme si, že v implementaci třetího příkladu -- naleznete jej v minulém čísle -- se skrývalo jedno nebezpečí: poslal-li někdo objektu třídy ModMN zprávu 'sub:' nebo 'add:' a jako parametr této třídy zadal objekt jiné třídy než ModMN, došlo nutně k běhové chybě. Implementace zpráv 'add:' a 'sub:' totiž objektu, který dostane jako parametr, posílá zprávy 'get1' a 'get2'; tyto zprávy jsou schopny zpracovat pouze objekty třídy ModMN. Pošleme-li objektu zprávu, kterou není schopen zpracovat, skončí program chybou.
Objective C nabízí pro takové případy řadu prostředků, které můžeme používat díky dědictví po standardní třídě Object. Ukážeme si použití dvou z nich:
// Objective C -- příklad 4
//
// dynamické rozpoznání třídy
// tento příklad ukazuje jak se můžeme při implementaci zpráv vyrovnat
// s tím, že objekt, předaný jako parametr zprávy, nemusí být objektem
// předem (staticky) určené třídy
| #import "sample3.h" | // importujeme interface |
| @implementation ModMN | // rodiče a proměnné není třeba uvádět -- jsou známy z interface |
... jako v příkladu 3 ...
-add:n
{
if ([n isKindOf:[ModMN class]])
return [self set:value+[n get1]:[n2 get]+[n get2]];
else if ([n isKindOf:[ModN class]])
return [self set:value+[n get]:[n2 get]+[n get]];
else {
printf("!!! přičítání nepoužitelného objektu !!!\n");
return self;
}
}
-sub:n
{
if ([n respondsTo:@selector(get1)] && [n respondsTo:@selector(get2)])
return [self set:value-[n get1]:[n2 get]-[n get2]];
else if ([n respondsTo:@selector(get)])
return [self set:value-[n get]:[n2 get]-[n get]];
else {
printf("!!! odčítání nepoužitelného objektu !!!\n");
return self;
}
}
... jako v příkladu 3 ...
@end
// end of file
Podívejme se podrobněji na jednotlivé části programu:
1. Standardní zprávy isKindOf a class -- sample4.m
V nové implementaci metody 'add:' nejprve ověříme je-li objekt 'n' objektem třídy ModMN (nebo kteréhokoli z jejích případných dědiců). K tomu slouží standardní zpráva 'isKindOf:'. Parametrem zprávy je identifikace třídy, kterou zjistíme tak, že třídě pošleme další standardní zprávu -- zprávu 'class'.
Implementace nejprve zjistí jedná-li se o objekt třídy (alespoň) ModMN a jestliže ano, přičte jej standardním způsobem. Pak ještě ověříme, zda se nejedná o objekt třídy ModN (nebo některého z jejích případných dědiců odlišných od třídy ModMN); ano-li, můžeme jej přičíst s využitím zprávy 'get'. Jinak ohlásíme chybu.
2. Standardní zpráva respondsTo a operátor @selector -- sample4.m
Pro implementaci zprávy 'sub:' využijeme jiný mechanismus: optáme se objektu přímo je-li schopen zpracovat určenou zprávu. K tomu slouží standardní zpráva 'respondsTo:'; jejím parametrem je identifikace požadované zprávy získaná pomocí standardního operátoru '@selector'.
Implementace metody 'sub:' tedy nejprve ověří je-li zadaný objekt schopen zpracovat zprávy 'get1' a 'get2'; ano-li, přičte číslo standardním způsobem. Ne-li, zjistíme, zná-li objekt alespoň zprávu 'get', abychom jej mohli přičíst jako objekt třídy ModN; jinak ohlásíme chybu.
Uvědomme si, že toto druhé řešení je o něco flexibilnější -- tentokrát můžeme pracovat s objekty libovolné třídy, jestliže jsou schopny zpracovat odpovídající zprávy. Nejsme tedy omezeni pouze na objekty třídy ModMN a jejích dědiců; zpráva 'sub:' korektně odečte například objekt reprezentující číselnou řadu, bude-li tento objekt schopen zareagovat na zprávu 'get' třeba vypočtením limity.