interface data type (tsz. interface data types)
Az absztrakt adattípus (ADT) és az interfész fogalma szorosan összefonódik az osztály absztrakciójával, és mindkettő kulcsszerepet játszik abban, hogy hogyan tervezünk bővíthető, karbantartható és jól strukturált kódot.
Egy absztrakt adattípus nem egy konkrét adatszerkezet, hanem egy logikai modell, amely az adatok tárolásának és manipulációjának módját írja le, függetlenül az implementációtól.
Példák absztrakt adattípusokra:
Egy ADT műveleteket definiál, de nem határozza meg, hogy ezek hogyan vannak implementálva. Például egy verem (stack) műveletei:
push(x)
– elem hozzáadásapop()
– legutolsó elem eltávolításatop()
– a legutolsó elem lekérdezése
A C++ nyelvben nincs beépített interface
kulcsszó (mint pl. Java-ban), de az interfész funkcionálisan megvalósítható absztrakt osztályokkal.
Egy interfészt C++-ban egy olyan osztályként definiálunk, amely csak tisztán virtuális függvényeket tartalmaz:
class IShape {
public:
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual ~IShape() {}
};
Ez az osztály egy interfészt definiál, amely bármilyen „alakzat” típusú objektum közös viselkedését írja le, anélkül, hogy tudnánk, hogyan működik a háttérben.
Az osztály absztrakciója azt jelenti, hogy elválasztjuk a „mit csinál” kérdést a „hogyan csinálja” kérdéstől.
class Stack {
public:
virtual void push(int x) = 0;
virtual int pop() = 0;
virtual bool isEmpty() const = 0;
virtual ~Stack() {}
};
Itt a Stack
egy absztrakt osztály, ami az absztrakt adattípus (verem) interfésze is egyben. Ez nem mondja meg, hogy az adatok egy tömbben vagy láncolt listában vannak tárolva – ezt majd a konkrét megvalósítás adja.
class ArrayStack : public Stack {
private:
int arr;
int topIndex;
public:
ArrayStack() : topIndex(-1) {}
void push(int x) override {
arr = x;
}
int pop() override {
return arr;
}
bool isEmpty() const override {
return topIndex == -1;
}
};
Itt az ArrayStack
egy konkrét implementációja a veremnek, amely egy tömböt használ.
A kód felhasználója azonban csak a Stack*
típuson keresztül éri el:
void process(Stack* s) {
s->push(10);
s->push(20);
cout << s->pop(); // 20
}
Ez lehetővé teszi a polimorfizmust: ugyanaz az interfész többféle implementációra alkalmazható.
Különválaszthatók az implementációk és az interfészek, ami jobb kódkarbantartást és kódújrafelhasználást tesz lehetővé.
Könnyen hozzáadhatsz új implementációkat anélkül, hogy a meglévő kódot módosítanád.
Mock objektumokkal tesztelheted az interfészt, anélkül hogy a konkrét megvalósítást használnád.
IShape
interfész használataclass Circle : public IShape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14 * radius * radius; }
double perimeter() const override { return 2 * 3.14 * radius; }
};
class Rectangle : public IShape {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override { return width * height; }
double perimeter() const override { return 2 * (width + height); }
};
void printShapeInfo(const IShape& shape) {
cout << "Area: " << shape.area() << endl;
cout << "Perimeter: " << shape.perimeter() << endl;
}
Ez a kód teljesen független attól, hogy shape
milyen konkrét osztályból jön. Ez absztrakció a javából.
Fogalom | Jelentés |
---|---|
ADT | Logikai leírás műveletekkel, de konkrét implementáció nélkül |
Interfész | Absztrakt osztály tisztán virtuális metódusokkal |
Absztrakció | A részletek elrejtése, és a lényeg kiemelése |
Polimorfizmus | Egy interfészen keresztül többféle viselkedés megvalósítása |
A C++ interfészek absztrakt osztályokon keresztül valósulnak meg. Egy absztrakt adattípus (ADT) meghatározza az elvárt viselkedést, míg az interfész ezt C++-ban deklarálja. Az osztály absztrakciója segít elrejteni az implementációs részleteket, és támogatja a moduláris, rugalmas és jól tesztelhető kódot.