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.



(obsah)


Copyright (c) Ondra Čada