Sissejuhatus abstraktsiooni
Sissejuhatus
Siiani oleme käsitlenud neljast objektorienteeritud programmeerimise põhiprintsiibist kolme:
- Kapseldamine ehk sisemiste andmete peitmine ja neile ligipääsu kontrollimine meetodite kaudu.
- Pärilikkus ehk uute klasside loomine olemasolevate põhjal käitumise taaskasutamiseks.
- Polümorfism ehk eri tüüpi objektide käsitlemine ühise tüübi kaudu.
Neljas ja kõige fundamentaalsem printsiip on abstraktsioon.
Abstraktsioon tähendab millegi teostuse detailide peitmist ning ainult selle avalikustamist, mida välisel maailmal on reaalselt selle kasutamiseks vaja. Eesmärk ei ole peita andmeid (seda eesmärki täidab kapseldamine), vaid peita kuidas midagi töötab, tehes samal ajal selgeks, mida see teeb.
Analoog elust enesest
Toome näiteks autojuhtimise. Auto rooli keeramisel pöördub auto vastavale suunale. Te ei pea teadma midagi roolisamba, hammasrataste ega selle all oleva hüdraulikasüsteemide kohta. Rool on abstraktsioon, sellega määratletakse, mida on võimalik teha (vasakule pöörata, paremale pöörata), ilma teadmiseta, kuidas see täpselt toimib.
Sama idee kehtib ka programmeerimises. Klass või tüüp saab määratleda, millised operatsioonid on võimalikud, ilma detailidesse süvenemata, see tähendab, et ei paljastata, kuidas neid operatsioone teostatakse.
Abstraktsioon Javas
Javas saavutatakse abstraktsioon kahe mehhanismi abil:
- Abstraktsed klassid ehk klassid, mis määratlevad osalise skeemi, jättes mõndade meetodite teostuse alamklasside teha
- Liidesed ehk lepingud, mis määratlevad, mida antud tüüp saab teha, ilma konkreetset teostust määramata.
Mõlemat käsitletakse järgmistes osades.
Abstraktsioon on tihedalt seotud teiste OOP-printsiipidega.
Kapseldamine peidab andmeid, abstraktsioon peidab teostust.
Polümorfism tugineb abstraktsioonile: kui mõne geomeetrilise kujundi implementatsioonil kutsuda välja shape.area(), ei oma tähtsust, milline konkreetne kuju arvutust teeb. Abstraktsioon defineerib tingimused (lepingu), polümorfism annab õige tulemuse käitusajal.
Miks abstraktsioon on oluline
Ilma abstraktsioonita peab iga koodiosa, mis kasutab mõnda klassi, teadma täpselt, kuidas see klass sisemiselt töötab. See seob klassid omavahel tihedalt kokku. Muutus ühes klassis võib põhjustada muutusi kõikjal, kus seda kasutatakse.
Abstraktsioon võimaldab luua stabiilse liidese erinevate programmi osade vahel. See vähendab komponentide omavahelist sõltuvust (low coupling) ja muudab süsteemi lihtsamini muudetavaks ja laiendatavaks. Kasutusel oleva klassi sisemine teostus võib vabalt muutuda, tingimusel et avalik leping jääb samaks.
Seda saab hästi illustreerida maksesüsteemi näitega:
// Without abstraction — the rest of the code depends on a specific implementation
PayPalPayment payment = new PayPalPayment();
payment.sendPayPalRequest(amount, apiKey);
// With abstraction — the rest of the code depends only on the concept of "a payment"
Payment payment = new PayPalPayment();
payment.pay(amount);
Esimesel juhul sõltub ülejäänud programm otseselt PayPalPayment klassist.
See tähendab, et makseviisi vahetamine nõuab muudatusi kõikjal, kus seda klassi kasutatakse.
Teisel juhul sõltub kood ainult Payment abstraktsioonist, mitte konkreetsest implementatsioonist.
Kui ettevõte otsustab hiljem kasutada näiteks StripePayment klassi, tuleb muuta ainult objekti loomise kohta:
Payment payment = new StripePayment();
Ülejäänud kood ei pea muutuma, sest see töötab jätkuvalt Payment abstraktsiooniga.
See muudab süsteemi paindlikumaks, lihtsamini laiendatavaks ja vähendab erinevate komponentide omavahelist sõltuvust.