A Kempelen Farkas Digitális Tankönyvtár/vagy más megjelenítő által közvetített digitális tartalmat a felhasználó a szerzői jogról szóló 1999. évi LXXVI. tv. 33. paragrafus (4) bekezdésében meghatározott oktatási, illetve tudományos kutatási célra használhatja fel. A felhasználó a digitális tartalmat képernyőn megjelenítheti, letöltheti, arról elektronikus adathordozóra vagy papíralapon másolatot készíthet, adatrögzítő rendszerében tárolhatja. A Kempelen Farkas Digitális Tankönyvtár/vagy más megjelenítő weblapján található digitális tartalmak üzletszerű felhasználása tilos, valamint kizárt a digitális tartalom módosítása és átdolgozása, illetve az ilyen módon keletkezett származékos anyag további felhasználása.

2.1.3. Irány az objektum!

Az adatstruktúra domináns fogalomként történő alkalmazásával – a fenti példához hasonlóan – sok esetben szellemes megoldáshoz juthatunk. Ha az így tervezett programot meg kell valósítanunk, akkor a példányosítás lehetőségének korlátai a láthatósági viszonyok megváltozását is eredményezhetik.

Példaként induljunk ki a korábban definiált veremből, amelyet az alábbi műveletekkel adtunk meg.

Művelet

Értelmezési tartomány

Értékkészlet

NEW

( )

verem

PUSH

(verem, elem)

verem

POP

(verem)

verem

TOP

(verem)

elem

EMPTY

(verem)

boolean

Amíg egyetlen példányunk van a veremből, addig a műveletekben a veremre való hivatkozás felesleges, hiszen a művelet nem vonatkozhat másra. Ebben az esetben a vermet implementáló adatstruktúra és a függvények egyetlen C programmodulban definiálhatók, amelyből a külvilág számára csak a verem.h-ban extern-ként megadott függvények láthatók.

        
VEREM.C:
   static struct Verem { ... };
   void New( ) { ... };
   void Push(struct elem * x) { ... };
   void Pop(struct elem * x) { ... };
   void Top (struct elem * x) { ... };
   BOOL Empty( ) { ... };

VEREM.H:
   extern void New( );
   extern void Push(struct elem * x);
   extern void Pop(struct elem * x);
   extern void Top (struct elem * x);
   extern BOOL Empty( );
        
        

A vermet használó program:

        
#include "verem.h"

struct elem { ... } a,b,c;
...
New( );
Push(&b);
...
Top(&c);
if (Empty( )) ...
        
        

Természetesen a verem számára definiálni kell az elem struktúrát, amit célszerű a program deklarációs (header) fájljából átvenni.

Ha két vagy több vermet használunk, akkor meg kell adni, hogy melyik veremre vonatkozzon a műveletünk. Ezt két módon tehetjük meg. Az egyik megoldás szerint annyi veremmodult hozunk létre különböző nevek alatt (például a műveletek neve elé illesztett előtaggal jellemezve: APush, BPush, illetve Averem, Bverem stb.), ahányra csak szükségünk van. Ennek a megoldásnak az a hátránya, hogy a programunkban csak statikusan deklarált vermeket használhatunk, hiszen a vermek neve és száma fordítási illetve szerkesztési időben kerül meghatározásra. Dinamikusan nem hozhatunk létre vermet, hiszen ez annyit tenne, hogy a programunkhoz futási időben kellene újabb modult kapcsolni. A megoldás előnye, hogy a vermek belső szerkezete rejtve marad. Elviekben a különböző vermekhez különböző implementációs módszerek tartozhatnak. Például az Averem lehet egy tömb, míg a Bverem dinamikus szerkezet.

A másik megoldás az, hogy egyetlen vermet implementáló függvény-csoportot hozunk létre, amelynek kívülről átadandó paramétere lesz a verem:

        
void New(struct Verem * v) { ... };
void Push(struct Verem * v; struct elem * x) { ... };
void Pop(struct Verem * v; struct elem * x) { ... };
void Top (struct Verem * v; struct elem * x) { ... };
BOOL Empty(struct Verem * v;) { ... };
        
        

Ezen megoldás előnye, hogy a függvények egyetlen példányban jelennek meg, ugyanakkor nagy hátránya, hogy megsértjük az információk elrejtésének elvét, hiszen az adatszerkezetek az alkalmazók számára láthatóvá válnak. Nemcsak láthatók, de a felhasználók felelősségévé válik a veremtípusú változók korrekt definiálása és kezelése.

A fenti példából látható, hogy az absztrakt adatszerkezetek alkalmazásának központi problémája a példányosítás, azaz miként tudunk definiált tulajdonságokkal (műveletekkel) leírt adatszerkezetekből tetszőleges számú példányt létrehozni. Példányosítást alkalmazunk akkor, ha például megadjuk az

int a, b;

kifejezést. Ekkor keletkezik két olyan példány (a és b néven) az int adatszerkezetből, amelyeken az int-re vonatkozó műveletek végrehajthatók. Az int belső szerkezete rejtett, az egyszerű felhasználás során nem szükséges ismernünk a tárolás szabályát (például byte-sorrend, vagy 2-es komplemens módszer). A műveletek a programozási nyelvekben a szabványos típusokhoz – így az int-hez is – előre meghatározottak.

A programozási nyelvek egy részében bevezették a felhasználó által definiálható felsorolás típusú változókat. Ezen egyszerű szerkezetek konstansainak nevét adhatja meg a felhasználó. A műveletek rögzítettek és nagyjából az értékadásra, az összehasonlításra, az előző és a következő érték választására, illetve az értéknek az egészek halmazára történő leképezésére korlátozódnak.

A programozási nyelvekben a változókon értelmezett és megvalósított műveletek megnevezésének és jelölésének értelmezése is fontos tanulságokkal szolgál. A nyelvekben implementált különböző típusokon végezhető műveleteket – általában akkor, ha szemantikailag azonos értelmű – azonos néven érhetjük el. A művelet elvégzésének módját és az eredmény típusát az operandus típusa határozza meg. Gondoljunk itt arra az esetre, hogy önmagában a "+" műveleti jelből nem dönthető el, hogy hatására milyen eredmény születik, hiszen az eredményt az határozza meg, hogy a műveletet milyen típus változón alkalmaztuk. Hasonló módon értelmezhetjük például a ki- és beviteli folyamatokat elvégző függvényeket és eljárásokat, amelyeknek különféle paraméterei lehetnek és az eredményük is a paraméterek típusaitól függ.

A programozási nyelvekben ábrázolt műveletek jelölésében is számos, nehezen magyarázható korláttal találkozunk. Ilyen a műveleti jel és az operandusok elrendezésének viszonya. Hagyományosan a kétoperandusú matematikai műveletek úgy ábrázoljuk, hogy a műveleti jel az operandusok közé kerül. Ezt infix jelölésnek nevezzük. Például:

a + b; cx / 5.23;      b1 or b2;    t * u;  egyik = másik;

A felhasználó által definiált szerkezeteken (változókon) csak részben engedélyezett az infix jelölés használata. Általában megengedett az értékadás és bizonyos esetekben a relációs operátorok (egyenlőség, kisebb, nagyobb) használata. Ugyanakkor, ha definiálunk például egy komplex szám típust, mint két valósat tartalmazó rekordot, azon nincs lehetőségünk az összegzés infix jelölésére. A komplex összegzést csak mint függvényt tudjuk definiálni, azaz a műveleti jelet (a függvény nevét) kötelezően az operandusok elé kell tenni. Ezt prefix jelölésnek nevezzük. Azaz

cmplplus(a, b)

A programnyelvek irányából közelítve célszerűnek látszik olyan szerkezetek kidolgozása, amelyek ugyanúgy példányosíthatók, mint a változók, ugyanakkor a rajtuk értelmezett műveletek is példányosodnak. Ez abban nyilvánul meg, hogy az elvégzendő műveletet nem csak a művelet jele vagy megnevezése határozza meg, hanem a művelet az adatszerkezettel együtt értelmezett.

Az említett tulajdonságokkal rendelkező szerkezeteket objektumoknak tekinthetjük.