„Az absztrakció lényege az adott kontextusban releváns információk megőrzése, valamint az ebben az összefüggésben irreleváns információk elfelejtése.” – John V. Guttag

A szoftverfejlesztésben és a számítástudományban az absztrakció olyan eljárás, amelynek során egy objektum, rendszer vagy folyamat kevésbé lényeges részleteit elhagyjuk vagy általánosítjuk, hogy a figyelem a vizsgált szempontból fontos tulajdonságokra irányuljon.

Az absztrakció jelentheti:

a fizikai, térbeli vagy időbeli részletek, illetve attribútumok eltávolításának vagy általánosításának folyamatát objektumok vagy rendszerek vizsgálata során; célja, hogy a figyelmet a nagyobb jelentőségű részletekre irányítsa; absztrakt fogalmak, modellek vagy reprezentációk létrehozását tárgyak, objektumok vagy rendszerek közös jellemzőinek kiemelésével.

Az absztrakció a számítástudomány és a szoftverfejlesztés egyik alapvető fogalma. Az absztrakciós folyamatot gyakran modellezésnek is nevezik, és szorosan kapcsolódik az elmélet és a szoftvertervezés fogalmához is. A modellek – a valóság bizonyos aspektusainak általánosításai – maguk is az absztrakció formáinak tekinthetők.

A számítástudományban használt absztrakció fogalma szorosan kapcsolódik a matematikai absztrakcióhoz, mivel az absztrakt fogalmakat önálló objektumokként kezeli. Az absztrakció emellett más területeken, például a művészetben használt fogalmakkal is rokonítható.

Az absztrakciók vonatkozhatnak valós objektumokra és rendszerekre, számítási rendszerek szabályaira vagy olyan programozási nyelvek szabályaira, amelyek maguk is absztrakciókat használnak vagy hoznak létre. Például:

adattípusok használata adatabsztrakció létrehozására, annak érdekében, hogy a programokon belüli adatstruktúrák működését el lehessen választani azok konkrét reprezentációjától; függvények, eljárások vagy alprogramok használata a vezérlési folyamat bizonyos részleteinek elrejtésére; az „absztrakciónak” nevezett szabályok alkalmazása, amelyek a lambda-kalkulus különböző változataiban a kifejezéseket szabad és kötött változók segítségével általánosítják; S-kifejezések használata adatstruktúrák és programok absztrakciójaként a Lisp programozási nyelvben; a közös viselkedés kiemelése nem absztrakt osztályokból „absztrakt osztályokba” öröklődés segítségével, ahogy az például az objektumorientált C++ és Java programozási nyelvekben is látható.

Alapok

A számítástechnika jelentős része a fizikai világtól elvonatkoztatva működik. A hardver olyan számítási modelleket valósít meg, amelyek bizonyos határok között felcserélhetők más modellekkel. A szoftver ezzel szemben olyan architektúrákra épül, amelyek lehetővé teszik, hogy az emberek nagy rendszereket hozzanak létre úgy, hogy egyszerre csak korlátozott számú problémára kelljen figyelniük. Ezek az architektúrák meghatározott absztrakciós döntésekre épülnek. Greenspun tizedik szabálya aforizmaként utal arra, hogy az ilyen architektúrák egyszerre elkerülhetetlenek és összetettek.

A számítástechnikában az absztrakció egyik központi formája a nyelvi absztrakció. Ennek során új mesterséges nyelvek jönnek létre a rendszer meghatározott aspektusainak kifejezésére. A modellező nyelvek például a tervezést segítik, míg a számítógépes nyelvek számítógéppel feldolgozható formában írják le a műveleteket.

Ezt az absztrakciós folyamatot jól szemlélteti a programozási nyelvek fejlődése: a gépi kódtól az assembly nyelveken át a magas szintű programozási nyelvekig. Minden szakasz absztrakciós rétegként szolgálhat a következő szakasz számára. A nyelvi absztrakció tovább folytatódik például szkriptnyelvekben és tartományspecifikus nyelvekben.

Egy programozási nyelven belül egyes nyelvi elemek lehetővé teszik a programozó számára, hogy újabb absztrakciókat hozzon létre. Ilyenek például az alprogramok, modulok, a polimorfizmus és a szoftverkomponensek. Más absztrakciók, például a szoftvertervezési minták, a fordítóprogram számára láthatatlanok maradnak, és elsősorban a rendszer megtervezését segítik.

Egyes absztrakciók célja, hogy korlátozzák azoknak a fogalmaknak a körét, amelyekkel a programozónak közvetlenül foglalkoznia kell. Ezt az absztrakció mögötti részletek elrejtésével érik el. Joel Spolsky szoftvermérnök és író bírálta ezt a megközelítést, és azt állította, hogy minden absztrakció szivárog, vagyis soha nem képes teljesen elrejteni az alatta lévő részleteket; ez azonban nem tagadja az absztrakció gyakorlati hasznosságát.

Egyes absztrakciókat úgy terveznek meg, hogy együttműködjenek más absztrakciókkal. Egy programozási nyelv például tartalmazhat idegen függvényinterfészt, amely alacsonyabb szintű nyelven írt kód meghívását teszi lehetővé.

Absztrakciós jellemzők

Programozási nyelvek

A különböző programozási nyelvek különböző típusú absztrakciókat biztosítanak, attól függően, hogy milyen alkalmazási területre készültek. Például:

Az olyan objektumorientált programozási nyelvekben, mint a C++, az Object Pascal vagy a Java, az absztrakció fogalma deklaratív nyelvi elemként is megjelenik. C++ nyelven például a function (parameters) = 0; szintaxis, Java nyelven pedig az abstract és interface kulcsszavak szolgálnak erre. Egy ilyen deklaráció után a programozó feladata olyan osztályt készíteni, amely megvalósítja a deklarált viselkedést, és példányosítható objektumokat hoz létre. A Rust nyelvben az absztrakció egyik központi eszköze a trait. A trait hasonló szerepet tölthet be, mint más nyelvekben az interfész: közös viselkedést ír le, amelyet több különböző típus is megvalósíthat. A Rust traitjei generikus programozással együtt statikus diszpécselést tehetnek lehetővé, amikor a fordító a konkrét típusokra specializált kódot állít elő. Bizonyos esetekben trait objektumokkal dinamikus diszpécselés is használható. A Rust tervezésében fontos szerepet kapnak az úgynevezett nulla költségű absztrakciók is: ezek olyan magas szintű nyelvi eszközök, amelyek használata ideális esetben nem jár többletfutásidejű költséggel. Például:

trait Ehető { fn kaloria(&self) -> f64; }

struct Alma { kaloria: f64, }

impl Ehető for Alma { fn kaloria(&self) -> f64 { self.kaloria

fn energia<T: Ehető>(etel: &T) -> f64 { etel.kaloria() }

Ebben a példában az Ehető trait az ehető dolgok közös viselkedését írja le, az Alma típus pedig megvalósítja ezt a viselkedést. Az energia függvény nem egy konkrét típushoz kötődik, hanem bármely olyan típusra alkalmazható, amely megvalósítja az Ehető traitet.

A funkcionális programozási nyelvek gyakran függvényekkel kapcsolatos absztrakciókat használnak, például lambda-absztrakciót – amely egy kifejezést valamely változó függvényévé alakít – vagy magasabb rendű függvényeket, amelyek más függvényeket is kaphatnak paraméterként. A Lisp programozási nyelvcsalád modern tagjai, például a Clojure, a Scheme és a Common Lisp, támogatják a makrórendszereket, amelyek szintaktikai absztrakciót tesznek lehetővé. Más programozási nyelvek, például a Scala, szintén tartalmaznak makrókat vagy hasonló metaprogramozási lehetőségeket. A Haskell esetében ilyen lehetőség a Template Haskell, az OCaml esetében pedig a MetaOCaml. Ezek segítségével a programozó kiküszöbölheti a sablonkódot, absztrahálhatja az ismétlődő függvényhívási mintákat, új vezérlési szerkezeteket vezethet be, és tartományspecifikus nyelveket hozhat létre. Helyes használatuk javíthatja a programozó hatékonyságát és a kód érthetőségét. A szintaktikai absztrakció következménye az is, hogy szinte bármely Lisp-dialektus, sőt elvileg sok más programozási nyelv is megvalósítható egy modern Lispben. Ez általában kevesebb, de a legtöbb esetben továbbra sem triviális erőfeszítést igényel a hagyományosabb programozási nyelvekhez – például a Pythonhoz, a C-hez vagy a Javához – képest.

Specifikációs módszerek

Az elemzők különféle módszereket dolgoztak ki a szoftverrendszerek formális meghatározására. Néhány ismert módszercsoport:

modellalapú absztrakt módszerek, például VDM és Z; algebrai technikák, például Larch, CLEAR, OBJ, ACT ONE és CASL; folyamatalapú technikák, például LOTOS, SDL és Estelle; nyomkövetési technikák, például SPECIAL és TAM; tudásalapú technikák, például Refine és Gist.

Specifikációs nyelvek

A specifikációs nyelvek általában valamilyen absztrakcióra támaszkodnak, mivel a specifikációkat rendszerint a projekt korai szakaszában, a megvalósításnál elvontabb szinten határozzák meg. Az UML specifikációs nyelv például lehetővé teszi absztrakt osztályok meghatározását. Ezek egy vízesésmodell szerint szervezett projektben a projekt architekturális és specifikációs szakaszában absztraktak maradhatnak.

Vezérlési absztrakció

A programozási nyelvek egyik fő feladata a vezérlési absztrakció biztosítása. A számítógépek alacsony szinten olyan műveleteket hajtanak végre, mint bitek mozgatása a memória egyik helyéről egy másikra, vagy bitsorozatok összeadása. A programozási nyelvek lehetővé teszik, hogy ezek a műveletek magasabb szinten legyenek kifejezhetők. Vegyük például a következő, Pascal-szerű módon írt állítást:

a := (1 + 2) * 5

Egy ember számára ez egyszerű és kézenfekvő számításnak tűnik: egy plusz kettő egyenlő hárommal, ezt megszorozva öttel tizenötöt kapunk. Az alacsony szintű lépések azonban, amelyek a művelet tényleges végrehajtásához és a 15 érték a változóhoz rendeléséhez szükségesek, összetettek. Az értékeket bináris számrendszerben kell ábrázolni, majd a számítást a fordítónak vagy értelmezőnek alacsony szintű utasításokra kell bontania. Ezek az utasítások a programozó számára sokkal kevésbé intuitívak: például bináris regiszterek eltolása vagy egyik regiszter tartalmának összeadása egy másik regiszter tartalmával nem felel meg közvetlenül annak, amit az ember hétköznapi értelemben „összeadásnak” vagy „szorzásnak” nevez. Végül a kapott értéket hozzá kell rendelni az a változóhoz, ami további, a programozó elől többnyire elrejtett lépésekkel jár, például a változó címének meghatározásával a fizikai vagy virtuális memóriában.

Vezérlési absztrakció nélkül a programozónak minden egyszerű összeadás, szorzás vagy értékadás esetén meg kellene adnia az összes regiszter- és bináris szintű lépést. Ez két súlyos következménnyel járna:

a programozónak újra és újra meg kellene ismételnie a gyakori műveletek alacsony szintű leírását; a program közvetlenül egy adott hardverhez és utasításkészlethez kötődne.

Strukturált programozás

A strukturált programozás az összetett programfeladatokat kisebb, kevésbé összetett részekre bontja. Ezeket világos vezérlési szerkezetek és jól meghatározott interfészek kapcsolják össze.

Egy egyszerű program esetében ennek célja lehet például annak biztosítása, hogy a ciklusoknak és az eljárásoknak egyértelmű kilépési pontjaik legyenek.

Nagyobb rendszerekben ez az összetett feladatok modulokra bontását jelenti. Vegyünk például egy rendszert, amely a hajókon és a parti irodákban dolgozók bérszámfejtését kezeli:

A legfelső szint tartalmazhat egy menüt a tipikus végfelhasználói műveletekkel. Ezen belül lehetnek önálló végrehajtható fájlok vagy könyvtárak olyan feladatokhoz, mint az alkalmazottak be- és kijelentkezése vagy a csekkek nyomtatása. Ezen összetevőkön belül több forrásfájl is lehet, amelyek mindegyike a probléma egy részét kezeli. A program többi része számára csak meghatározott interfészek érhetők el. Egy bejelentkezési programnak például külön forrásfájljai lehetnek az adatbeviteli képernyőkhöz és az adatbázis-kapcsolathoz. Az adatbázis-kapcsolat maga is lehet önálló, harmadik féltől származó könyvtár vagy statikusan összekapcsolt könyvtárrutinok halmaza. Az adatbázisnak vagy a bérszámfejtő alkalmazásnak el kell indítania a hajó és a part közötti adatcsere folyamatát. Ez az adatátviteli feladat gyakran további összetevőkből áll.

Ezek a rétegek elszigetelik egymástól az egyes komponensek megvalósítási részleteit. Az objektumorientált programozás ezt a megközelítést továbbfejleszti és kiterjeszti.

Adatabsztrakció