ZpětObsahDalší

Objective C: příklad 3
Dědičnost a vkládání objektů


Třetí příklad ukazuje, jak můžeme vytvořit novou třídu reprezentující dvojice čísel modulo M, N na základě hotové třídy z minulého příkladu.

// Objective C -- příklad 3/1, soubor sample3.h
//
// ukázka dědičnosti a skládání objektů

#import "sample2.h"

// nová třída bude reprezentovat dvojice čísel v algebře modulo MxN
@interface ModMN: ModN // třída se jmenuje ModMN a je dědicem třídy ModN
{ // její objekty obsahují proměnné
  ModN *n2; // druhá složka dvojice (vložený objekt)
}
+(ModMN*)modMNWithM:(int)M N:(int)N; // vytvoření nového objektu, nastavení modulů
-initWithM:(int)M N:(int)N; // inicializace nového objektu, nastavení modulů
-set:(int)i:(int)j; // nastavení na celočíselnou hodnotu <i,j>
-(int)get1; // zjištění hodnoty první složky
-(int)get2; // zjištění hodnoty druhé složky
@end
// end of file

Podívejme se podrobněji na jednotlivé části programu:

1. Rozhraní třídy -- sample3.h

Hlavičkový soubor 'sample2.h' musíme importovat, chceme-li vytvářet novou třídu jako dědice třídy ModN. Standardní hlavičkový soubor 'Foundation.h' je importován nepřímo prostřednictvím souboru 'sample2.h', nemusíme jej tedy zde importovat znovu (ale kdybychom to udělali, vůbec nic by se nestalo).

Novou třídu implementujeme tak, že její objekty budou díky dědictví po třídě ModN samy reprezentovat první složku dvojice čísel; každý objekt si navíc automaticky vytvoří další pomocný objekt třídy ModN, který bude reprezentovat druhou složku dvojice. Přidáme tedy pouze jedinou proměnnou n2, která bude obsahovat identifikaci tohoto pomocného objektu.

// Objective C -- příklad 3/2
//
// ukázka implementace nové třídy s dědičností a skládáním objektů

#import "sample3.h"

@implementation ModMN
-(int)get1
{
  return value;
}
-(int)get2
{
  return [n2 get];
}
-set:(int)i
{
  return [self set:i:i];
}
-set:(int)i:(int)j
{
  [super set:i];
  [n2 set:j];
  return self;
}
-add:n
{
  return [self set:value+[n get1]:[n2 get]+[n get2]];
}
-sub:n
{
  return [self set:value-[n get1]:[n2 get]-[n get2]];
}
-initWithM:(int)m N:(int)n
{
  [super initWithN:m];
  n2=[[ModN modNWithN:n] retain];
  return self;
}
-initWithN:(int)m
{
  return [self initWithM:m N:m];
}
+(ModMN*)modMNWithM:(int)m N:(int)n
{
  return [[[self alloc] initWithM:m N:n] autorelease];
}
// kvůli uvolnění vloženého objektu musíme reimplementovat metodu dealloc!!!
-(void)dealloc
{
  // nejprve uvolníme vložený objekt
  [n2 release];
  [super dealloc];
}
@end
// end of file

Podívejme se podrobněji na jednotlivé části programu:

2. Implementace metod -- sample3.m

Implementace metod je přesnou obdobou implementace funkcí v běžném jayzce C: zopakujeme hlavičku metody, a namísto středníku za ni zapíšeme blok obsahující tělo metody.

Implementace metody get1 ukazuje, že metody mají přímý přístup i ke zděděným proměnným objektu -- použijeme-li tedy uvnitř metody proměnnou value, budeme pracovat s obsahem proměnné value, zděděné po třídě ModN. Poznamenejme, že Objective C nabízí direktivu @private, kterou lze takový přístup k proměnným zakázat (požadujeme-li maximální flexibilitu); kdyby tomu tak bylo, museli bychom použít výraz [super get] podobně, jako je v metodě set:: použit výraz [super set:i].

Metoda get2 je implementována očekávatelným způsobem. Metoda set: je implementována prostřednictvím metody set:: tak, aby nastavila obě složky na stejnou hodnotu. Konečně sama metoda set:: prostě nastaví obě složky pomocí metody set:, implementované v původní třídě ModN -- v prvém případě to zajistí speciální příjemce super, ve druhém fakt, že objekt n2 je objektem třídy ModN.

Nyní se vrátíme k implementaci metody zero ve třídě ModN (v minulém příkladu). Kdybychom ji byli implementovali přímo příkazem value=0, jak je zvykem v C++, museli bychom ji nyní reimplementovat. Použili jsme však mnohem flexibilnější a skutečně objektový přístup [self set:0]; díky tomu můžeme zprávu zero poslat i objektu třídy ModMN a bude pracovat naprosto korektně. Uvědomme si, proč tomu tak je:

Metody add a sub pak využívají metody set::. Rozeberte si tyto metody podrobně a uvědomte si, které objekty dostávají které zprávy a použije-li se implementace z třídy ModN nebo implementace z třídy ModMN!

Metoda initWithM:N: si nejprve vyžádá inicializaci zděděných proměnných příkazem [super initWithN:m] a pak vytvoří pomocný objekt pro druhou složku dvojice a jeho identifikaci uloží do proměnné n2. Zpráva retain je informace pro garbage collector, aby tento objekt neuvolňoval, protože si jej chceme sami podržet.

Metodu initWithN: reimplementujeme prostřednictvím metody initWithM:N:. Uvědomme si, že -- podobně jako tomu bylo u zprávy zero -- stačilo reimplementovat zprávu initWithN: a objekty třídy ModMN budou korektně zpracovávat i zprávu init (protože v její implementaci objekt sám sobě pošle zprávu initWithN:). Pokud bychom vytvářeli ještě další novou třídu jako dědice třídy ModMN, stačilo by uvnitř ní reimplementovat jedinou metodu initWithM:N:, aby objekty nové třídy korektně interpretovaly všechny zprávy init, initWithN: i initWithM:N:.

Na implementaci 'třídní' metody modMNWithM:N: není nic zvláště zajímavého. Uvědomme si, že díky reimplementaci metody initWithN: a díky korektní práci s objekty můžeme třídě ModMN posílat i zprávu modNWithN:, zděděnou od třídy ModN, aniž bychom ji museli reimplementovat.

Protože máme uvnitř objektu další, vnořený objekt, musíme připravit ještě metodu dealloc -- v jejím rámci řekneme pomocí zprávy release garbage collectoru, že již nebudeme potřebovat vložený objekt n2 a pak si teprve příkazem [super dealloc] vyžádáme uvolnění objektu, který zprávu zpracovává.

// Objective C -- příklad 3/3
//
// ukázka použití nové třídy

#import "sample3.h" // importujeme interface

void main()
{
  NSAutoreleasePool *pool=[NSAutoreleasePool new]; // garbage collector
  ModMN *n1,*n2;
  n1=[ModMN new:4:6];
  n2=[ModMN new:20:12];
  // inicializujeme obě čísla stejně, vzhledem k různým modulům však bude
  // nastavená hodnota různá:
  #define INIT1 30
  #define INIT2 40
  [n1 set:INIT1:INIT2];
  [n2 set:INIT1:INIT2];
  printf("Init <%d,%d>, n1=<%d,%d>, n2=<%d,%d>\n",INIT1,INIT2,[n1 get1],[n1 get2],[n2 get1],[n2 get2]);
  // vypíše "Init <30,40>, n1=<2,4>, n2=<10,4>"
  // přičteme k prvnímu číslu druhé
  [n1 add:n2];
  printf("n1+n2=<%d,%d>\n",[n1 get1],[n1 get2]);
  // vypíše "n1+n2=<0,2>"
  [pool release];
}
// end of file

Použití nových metod by již mělo být jasné ze zdrojového kódu.


ZpětObsahDalší

Copyright (c) Chip, O. Čada 2000