Využití předdefinovaných tříd (knihovny tříd NeXTstepu)
V prvním příkladu si ukážeme nejjednodušší příklad OOP, spíše 'programování s objekty' než skutečně objektové programování: vytvoříme jednoduchý program, který využívá hotových tříd podobně, jako neobjektové programy využívají knihovních služeb.
Program bude pracovat jako velmi jednoduchá databáze, ukládající dvojice textových řetězců; první z textů bude sloužit jako klíč pro vyhledávání, druhý reprezentuje hodnotu. Využijeme zcela triviální uživatelské rozhraní na úrovni příkazové řádky; pro ukládání dat použijeme standardní třídu NXStringTable.
// Objective C -- příklad 1
//
// využití předdefinovaných tříd (knihovny tříd NeXTstepu)
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <sys/file.h>
#import <objc/NXStringTable.h>
| #define STOREFILE | "sample1.data" |
void main()
{
| id seznam; | // objekt reprezentující seznam řetězců |
| char buffer[512]; | // vstupní textový buffer |
seznam=[NXStringTable new];
if (!access(STOREFILE,R_OK)) {
[seznam readFromFile:STOREFILE];
printf("Načteno %d položek\n\n",[seznam count]);
}
// jednoduchá nápověda (uživatelské rozhraní programu by samozřejmě
// zasluhovalo vylepšení):
printf("klíč,hodnota / ?klíč / * / !\n");
// hlavní cyklus, do příkazu switch vstoupí každý zadaný řádek
for (;gets(buffer);) switch (*buffer) {
// vykřičník program ukončí
case '!': goto Konec;
// hvězdička opíše všechny uložené dvojice
case '*': {
int i=0;
NXHashState iter=[seznam initState];
void *k,*v;
printf("Tabulka obsahuje:\n");
while ([seznam nextState:&iter key:&k value:&v])
printf("%3d %s, %s\n",++i,(char*)k,(char*)v);
printf("*** konec (%d položek)\n",i);
}
break;
// otazník vyhledá hodnotu k zadanému klíči
case '?': {
char const *cp=[seznam valueForStringKey:buffer+1];
if (cp==NULL) printf("Není v seznamu!\n");
else printf("> %s\n",cp);
}
break;
// jinak vkládáme novou dvojici
default: {
// vyhledáme čárku oddělující oba řetězce a ...
char *cp=strchr(buffer,',');
if (cp==NULL) {
Chyba:
printf("Chyba!!!\n");
break;
}
// ... nahradíme ji nulovým znakem
*cp++=0;
// nalezneme první nemezerový znak
while (NXIsSpace(*cp)) cp++;
if (*cp) {
[seznam insertKey:NXCopyStringBuffer(buffer)
value:NXCopyStringBuffer(cp)];
printf("Položka %s, %s přidána na pozici %d\n",
buffer,cp,[seznam count]);
} else goto Chyba;
}
break;
}
Konec:
[seznam writeToFile:STOREFILE];
[seznam free];
printf("hotovo\n");
}
// end of file
Podívejme se podrobněji na jednotlivé části programu:
1. Import hlavičkových souborů
Použití direktivy #import namísto 'obyčejné céčkové' direktivy #include nás zbaví problémů s případným vícenásobným překladem vnořených hlavičkových souborů. V tomto případě je samozřejmě lhostejné kterou direktivu použijeme, protože standardní 'céčkové' hlavičkové soubory jsou proti vícenásobnému překladu ochráněny pomocí vložených direktiv #ifdef (direktiva #import pouze o něco málo zrychlí překlad).
2. Proměnná seznam a její inicializace
Proměnná 'seznam' je typu 'id', může tedy reprezentovat jakýkoli objekt. Požadovaný objekt třídy NXStringTable vytvoříme tak, že třídě pošleme zprávu 'new'; třída vytvoří objekt a vrátí jeho identifikátor. Ten uložíme do proměnné 'seznam'.
3. Otevření souboru
Jestliže existuje soubor s databází řetězců ('if (!access...'), vyžádáme si od objektu 'seznam' aby obsah souboru načetl zprávou 'readFromFile:'. V takovém případě informujeme uživatele o počtu načtených položek; počet nám objekt sdělí pošleme-li mu zprávu 'count'.
4. Vkládání údajů do databáze
Uvnitř příkazu 'switch', rozlišujícího různé formy vstupu, se nejprve podíváme na obsah větve 'default:', která nám umožňuje vkládat do databáze nové dvojice údajů.
Oba zadané řetězce jsou odděleny čárkou. Prvních několik příkazů pouze jednoduchým způsobem rozdělí řetězec na dva -- čárku prostě nahradíme nulovým znakem (který tedy ukončí klíč) a vyhledáme první nemezerový znak za čárkou (tam začíná hodnota).
Vložení řetězců do databáze si vyžádáme pomocí zprávy 'insertKey:value:'. Objekt třídy NXStringTable zařadí odkazy na oba řetězce do své databáze tak, abychom mohli podle klíče snadno vyhledávat (objekt spravuje hashovanou tabulku s hash funkcí optimnalizovanou pro obecné textové řetězce). Do databáze se ukládají adresy řetězců, ne řetězce samy; musíme je proto zduplikovat pomocí služby 'NXCopyStringBuffer'.
5. Vyhledávání údajů v databázi
Zadáme-li řetězec začínající otazníkem, vyžádá si program od objektu 'seznam' vyhledání hodnoty odpovídající klíči ve zbytku vstupního řetězce -- objekt tuto službu vykoná po přijetí zprávy 'valueForStringKey:'.
6. Prohledání celé databáze
Objekt třídy NXStringTable umožňuje i sekvenční procházení. Využívá pro něj speciální typ NXHashState; hodnoty tohoto typu reprezentují stav procházení (tj. 'jak daleko jsme zatím došli'). I požadavky na sekvenční procházení jsou objektu samozřejmě předávány prostřednictvím zpráv.
7. Ukončení programu
Nakonec pošleme objektu 'seznam' zprávu 'writeToFile:', aby zapsal svůj nový obsah do souboru, a zprávu 'free' aby sám sebe zrušil.