Zpět | Obsah | Další |
Čtvrtým příkladem není kompletní program, ale pouze doplněk k minulému příkladu. Uvědomme si, že v implementaci minulého příkladu 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.
Jistě, mohli bychom v duchu jazyka C++ deklarovat typy objektů:
@interface...
...
-(ModMN*)add:(ModMN*)o;
-(ModMN*)sub:(ModMN*)o;
...
@end
to by však ve skutečnosti nic neřešilo. Jistě, překladač by mohl kontrolovat, zda objekt, který metodě předáváme, je deklarován stejně; jenže bez ohledu na to jak je objekt deklarován může jeho obsahem být cokoli!
Objective C nabízí pro takové případy řadu prostředků, jež na rozdíl od nedostatečného přístupu C++ problém skutečně řeší; máme je k dispozici jako metody libovolného objektu díky dědictví po standardní třídě NSObject. 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 isKindOfClass:[ModMN class]])
return [self set:value+[n get1]:[n2 get]+[n get2]];
else if ([n isKindOfClass:[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 respondsToSelector:@selector(get1)] && [n respondsTo:@selector(get2)])
return [self set:value-[n get1]:[n2 get]-[n get2]];
else if ([n respondsToSelector:@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:
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 isKindOfClass:. 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; program však pořád pracuje dál a nezhroutí se -- na rozdíl od C++.
Pro implementaci zprávy sub: využijeme jiný mechanismus: optáme se objektu přímo je-li schopen zpracovat určenou zprávu (o něčem podobném se už C++ nezdá vůbec). K tomu slouží standardní zpráva respondsToSelector:; 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 ještě mnohem 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.
Zpět | Obsah | Další |
Copyright (c) Chip, O. Čada 2000