Az objektum-orientált programozás egy tervezési filozófia komplex szoftverek írásához. A központi eszköze az absztrakt adattípus, ami lehetővé teszi a magasabb szinten való gondolkodást és programozást, mint pusztán a számok és számtömbök. A matematikusok és a fizikusok világában ismerős dolog az absztrakció ereje, például fizikai egyenletekben az összes komponenes kiírása helyett gyakran használnak külön erre bevezetett operátorokat. De ilyen jellegű absztrakciókat ritkán használunk programozás során.
Az OOP számos fogalmat tartalmaz, ami hasznosnak bizonyul nagy projektek implementálásában. Ezek:
Talán a legfontosabb fogalom mind közül az információrejtés. Ez azt jelenti, hogy egy olyan információ, amelyre csak egy eljárásban van szükség, azt nem szabad más olyan eljárások tudomására hozni, amiknek nincs szükségük erre az információra. Mint a CIA-ben, az eljárásokat csak azokról az adatokról kell informálni, amelyekre feltétlenül szükségük van. Ez a filozófia egyszerűsíti a programozást, mert kevesebb részlettel kell foglalkozni, és kisebb a hibalehetőség esélye is.
Az információrejtés filozófiájának egyik megvalósító eszköze, hogy az adatot egy származtatott típusba burkoljuk, és csak bizonyos eljárásokon keresztül (gyakran metódusoknak hívjuk őket) engedjük módosítani őket.
Ez a beburkolás lehetővé teszi a problémák szétválasztását. Elkülönülten lehet írni egy nagy program darabjait anélkül, hogy foglalkozni kellene egy új eljárás által egy régin okozott sérülésre. Komplex programok írása ezáltal egy N nagyságrendű problémává válik N*N probléma helyett. Nézzünk egy példát arra, hogy ez mit is jelent. Figyeljük meg a következő interfészt, ami egy Fortran77 örökség:
Ebben az eljárásban f az adat, amin a műveletet kell végrehajtani, t egy ideiglenes munkatömb, mixup egy bit cserélő tábla, sct egy szinusz/koszinusz tábla, indx az átalakítás hosszát adja meg, nx az f mérete, nxh a megmaradó adat hossza és isign vagy a transzformáció iránya (-1 vagy 1) vagy a tábla inicializálására vonatkozó kérés (0).
Ahhoz, hogy használhassuk ezt az eljárást, az összes adatot helyesen át kell adni, és ez rengeteg hibalehetőséget rejt magában. Mindamellett a legtöbb paraméter csak a belső működési részletek megadásáért felelős. A programozó csak magával az adattal (f) és az iránnyal (isign) szeretne foglalkozni. Az élet sokkal egyszerűbb lenne, ha pusztán a következő módon lehetne hívni az eljárást:
anélkül, hogy a többi részlettel kellene foglalkozni.
Az egyik oka annak, hogy mindezek a részletek nincsenek elfedve, az, hogy a Fortran77 nem támogatta dinamikus tömbök használatát. Automatikus tömbök használatával könnyedén el lehet rejteni az ideiglenes számoló tömböt (t), a mixup és a sct táblákat egy csomagoló függvénybe:
Itt a programozónak nem kell törődnie ezekkel a dolgokkal többé, és így kevesebb lehetősége van a hibázásra.
A Fortran95 tömbök magukba foglalják a dimenzióra vonatkozó információt is, ezáltal kivehetünk minden dimenzióra vonatkozó információt az interfészből:
Sikeresen elrejtettük azokat a részleteket a programozó elől, amiket nem szükséges tudnia ahhoz, hogy használja az eljárást. Így az interfész sokkal egyszerűbb lett és kevesebb hibalehetőséget rejt magában:
Ha belegondolunk az interfész igazi jelentőségébe, akkor látható, hogy valószínűtlen, hogy megváltozik a jövőben, még akkor is, ha az eljárás belső részletei megváltoznak. Például tegyük fel, hogy adott egy számítógép, ahol egy optimalizált változata adott az eljárásnak, ami sokkal gyorsabb, mint az örökölt fft1r. Ekkor könnyen ki lehet cserélni az eljárás hívását a csomagoló függvényben, és a csomagolófüggvény használóinak semmit nem kell változtatniuk a kódjukon.
Ez a beburkolás lehetővé teszi az implementációs részletek változtatását anélkül, hogy a program többi részén változtatni kellene. Ez lehetővé teszi a konkurens programozást is: különböző programozók módosíthatják egy nagy program különböző részeit, anélkül, hogy egymás útjában állnának, mindaddig, amíg az interfészek nem változnak.
Tovább tökéletesíthetjük ezt az interfészt, ha észrevesszük, hogy az indx argumentum, ami az fft hosszát határozza meg, valamint a mixup és sct belső tábláknak egymással konzisztenseknek kell lenniük. A tábláknak akkor kell létrejönniük, ha az isign paraméter értéke 0:
De ha az fft később hívódik meg, akkor az indx paraméter különbözhet.
A probléma egy része itt abból adódik, hogy az örökölt fft1r-t két teljesen különböző művelet végrehajtására használjuk: tábla inicializálásra és transzformációra. Jobb ha két különböző függvényünk van a két különböző műveletre.
De a táblák az csomagoló ff1 csomagoló függvény belsejében eltárolt privát tömbök. Hogyan tudja egy másik eljárás inicializálni ezt a táblát? Ennek megoldására több út létezik. Az egyik, hogy a táblákat egy modulba tesszük, ami a modul összes eljárása számára hozzáférhető:
Ezeket az eljárásokat a következő módon lehet használni:
Szükségünk van egy harmadik eljárásra is a modulban, ami felszabadítja a táblákat, ha nem hajtunk végre több fft-t.
Egy másik lehetőség egy hozzáférésvezérlő hozzáadása. Ha a következő sorokat írjuk a modul elejére:
akkor az fft táblákhoz nem lehet hozzáférni a modulon kívülről. A tábla manipulálásának egyetlen módja a new_fft_table és a delete_fft_table eljárások meghívása.
Gyakorlásképpen gondolkozzunk további hibaellenőrzéseken, amiket az eljárások belesejébe lehetne tenni, például hogyan lehetne megelőzni egy még nem létrehozott fft táblára vonatkozó fft hívást.
Ezzel az összes fft-khez tartozó műveletetet egyetlen modulba csoportosítottunk. Ilyenfajta csoportosítás szintén része az egységbezárás koncepciójának. Az információrejtés és az adatbeburkolás vitathatatlanul a leghasznosabb és legfontosabb fogalmak az objektum-orientált programozásban.
A függvény túlterhelés egyazon eljárásnévvel különböző műveletek végrehajtását jelenti, ami az argumentumtípusok különbözőségén alapul. A Fortran77-ben már megvolt ez a lehetőség. Például a real() függvény különböző dolgokat jelentett a típustól függően.
Fortran95-ben a generikus függvények ezt lehetővé teszik felhasználó által definiált függvények esetén is. Például több különböző típusú FFT léthet: valós->komplex, komplex->komplex, egydimenziós, kétdimenziós, egyszeres pontosságú, kétszeres pontosságú, stb. Fortran77-ben különböző neveket kellett adnunk minden FFT-nek. Amíg egyértelmű, hogy ezek mindegyike mit csinál, a Fortran95 megengedi, hogy ugyanazt a nevet használjuk mindegyikre, generikus interfészek használatával:
mindaddig, amíg a függvények mindegyike különböző argumentumtípusokkal rendelkezik.
és így tovább.
Emellett könnyű túlzásba vinni a függvény túlterhelést. Csak olyan esetekben kell használni, amikor nyilvánvaló, hogy mit fejez ki. Kerülni kell a használatát olyan esetekben, ha az csak összezavarja a szándékunkat. Például különböző eljárásokat túlterhelni egy "megold" generikus nélvvel esetleg azt eredményezheti, hogy később nem fogunk emlékezni, hogy éppen melyik megoldásra gondoltunk, anélkül, hogy gondosan megtanultuk volna az összes argumentum típusát a modulban. Ezenkívül cél az is, hogy más ember is képes legyen elolvasni a kódot és könnyen megértse, hogy az mit akar csinálni.
A függvény túlterhelést gyakran statikus polimorfizmusnak nevezik, mert annak eldöntése, hogy éppen melyik függvényt kell hívni, fordítási időben történik, nem pedig futási időben.
Egy absztrakt adattípus vagy osztály egységbe zár egy felhasználó által definiált típust annak műveleteivel, amik a típuson hajtódnak végre. Például vegyünk egy osztályt, amit Alkalmazott-nak hívunk, és legyen a feladata adatbázis rekordok manipulálása. Az adat, amit tartalmaz: egy személy azonosítója és a neve. A függvények, amiket végre lehet hajtani rajtuk: egy új rekord létrehozása vagy egy meglévő törlése, egy rekord kinyomtatása és egy rekordból az azonosító kinyerése. Ez Fortran95 osztállyal megvalósítva így néz ki:
Ennek a típusnak egy változóját objektumnak nevezzük. Ezt a következőképpen lehet deklarálni:
A származtatott típus komponenseit az osztály adattagjainak hívjuk. Ezek gyakran private-ként vannak deklarálva, azaz ezek a komponensek nem hozzáférhetőek az osztályon kívülről. Az osztályban definiált eljárásokat tagfüggvényeknek hívjuk. Általában csak ezeken keresztül lehet az Alkalmazott objektumokat manipulálni. Egy függvény, amit mindig szükséges definiálni, a konstruktor, ami a rekord inicializálására szolgál. Például:
Megadhatunk egy személy rekordot a következőképpen:
Konvenció szerint az első argumentum minden metódusban az osztály típusa, és rendszerint this-sel hivatkoznak rá (C++-ban) vagy self-fel (más objektum-orientált nyelvekben). A legtöbb ilyen nyelvben az első argumentum nincs explicit deklarálva, de elérhető.
A destruktor gyakran egy Alkamazott objektum törlésére szolgál. Fortran95-ben ez csak akkor szükséges, ha az Alkalmazott típusnak van mutató komponense. A mi esetünkben a destruktort az adat kinullázására használjuk:
Egy rekordot a következőképpen lehet törölni:
Mivel az Alkalmazott típus komponensei privát adattagok, nem lehet őket közvetlenül kiiratni:
Helyette fel kell venni egy metódust, ami már eléri a privát adattagokat, például:
Bár talán szükségtelenül bonyolultnak tűnik a komponensek priváttá tétele, megvan az az előnye, hogy meg lehet változtatni a komponenseket anélkül, hogy az osztályt használó kódon változtatni kellene. Például tegyük fel, hogy később egy dátum adattagot szeretnénk felvenni az Alkalmazott típusba:
Ehhez új konstruktor kell:
Továbbra is használhatjuk a régi konstruktort is, generikus függvények használatával. Először is átnevezzük a régi konstruktort:
Aztán definiálhatunk generikus interfészt:
így a new_Alkalmazott névnek most már két jelentése van.
Az összes régi kód ugyanúgy működik, mint azelőtt, és az új kód kihasználhatja az új lehetőséget:
Akár a print metódus működését is megváltoztathatjuk, tegyük fel, hogy azt szeretnénk, hogy egy fájlba írjon a konzol helyett. Ezt a metódust is megváltoztathatjuk ennek megfelelően.
Egy osztály nyilvános interfésze egy absztrakt típust reprezentál a külvilágnak. Azzal, hogy az osztály megköveteli a külvilágtól, hogy csak ezeket az interfészeket használja, az osztály belső részleteit privátnak tartja meg, a belső adat nem rontható el, és a metódus implementációja megváltoztatható másokkal való ütközés nélkül.
Ennek a dokumentumnak az eredeti (angol nyelvű) változata megtalálható a következő címen: http://exodus.physics.ucla.edu/Fortran95/scf95.lect5.pdf
Fortran2003-ban az EXTENDS kulcsszóval lehet örökölni. A kulcsszó után zárójelbe téve következik a szülő:
Az az osztály, ami nem örököl semmiből, alaptípus is egyben.
Az osztálynak megvannak azok az adattagjai, amik a szülő osztályban is megvoltak, de lehetnek új adattagjai is. Az osztály a szülő metódusait is örökli. Az öröklött adattaok többféleképpen is hozzáférhetők:
Fortran2003-ban az osztályok magukba foglalhatják metódusaikat. Ekkor a CONTAINS kulcsszó választja el őket az adattagoktól:
ahol is a metódusok szintaktikája:
ahol:
ahol is a PUBLIC jelentése: nyilvános, a PRIVATE: rejtett, a NON_OVERRIDABLE: nem felülírható. A NOPASS esetén az objektum, mint a metódus argumentuma nem adódik át automatikusan, azt híváskor külön meg kell adni, míg a PASS egy másik argumentumot ad át automatikusan.
A polimorfizmus azt jelenti, hogy egy adat vagy egy eljárás (függvény, szubrutin) típusa megváltozhat. Alapja az, hogy a származtatott osztály példánya a szülő osztálynak is példánya, így mindenütt használható, ahova a szülőosztály példányát várjuk. A Fortran2003-ban ez a lehetőség a CLASS kulcsszóval érhető el:
Ez a pointer a későbbiekben rectangle vagy square objektumokra is mutathat, vagy bármely később definiált, a shape osztályból leszármazott objektumra.
A polimorfizmus függvényekre és szubrutinokra is átvihető:
A polimorfizmus miatt ez a szubrutin nemcsak a shape osztály példányaira, hanem minden, a shape osztályból leszármazott osztály példányaira is hívható.
A futás idejű típus a SELECT TYPE szerkezettel érhető el:
A Fortran2003-ról szóló rész forrása