Šta je objektno orjentisano programiranje?
Objektno orjentisano programiranje ili skraćeno “OOP” je način organizovanja koda oko manjih celina pod nazivom “objekat”. Objektno orjentisano programiranje pomaže u strukturiranju koda u razumljive i samostalne jedinice koje se mogu lako ponovno koristiti u različitim delovima programa. Primenom objektno orjentisanog programiranja se dobija pregledniji i razumljiviji programski kod koji je jednostavan za održavanje!
Šta je objekat?
Objekat u programiranju ima dosta sličnosti sa ljudskim poimanjem objekta te je upravo zbog toga i dobio takav naziv. Svaki objekat ima neke svoje osobine (svojstva) i funkcionalnost sa kojom može da izvršava zadatke.
Objekat u programiranju predstavlja mali deo programskog koda koji čuva odredjene podatke i ima funkcionalnost da izvršava neke prethodno pripremljene akcije.
Karakteristike objekta (“Svojstva”)
Dobar kandidat za primer objekta u programiranju je “Vozilo” (npr. vozilo u nekoj igrici). Vozilo u realnom životu ima baš puno karakteristika (‘svojstava’) ali ćemo u ovom primeru pomenuti samo par karakterističnih kao što su: “marka vozila”, “tip motora”, “godište proizvodnje”.
Funkcionalnosti objekta (“Metode”)
Vozilo ima mnogo funkcionalnosti kao što su: “prevoz putnika”, “prevoz materijala”, “grejanje putnika”, “osvetljavanje”… Da bi se izvršila funkcionalnost “prevoz putnika” potrebno je uraditi čitav niz akcija: otključati vrata, okrenutli ključ, ubacili u brzinu, dati gas… Sve pomenute sitne akcije koje prethode ispunjavanju funkcionalnosti predstavljaju odličan primer za grupisanje. Grupisanje programerskog koda koje je zaduženo za izvršavanje jedne funkcionalnosti objekta se piše u još manjoj programskoj celini koja se naziva metoda objekta.
Kreiranje objekata (“Klase”)
U običnom životu da bi se napravio neki objekat potreban je njegov projekat ili plan na osnovu čega bi smo ga mi napravili. Isto važi i kod programiranja, šablon za kreiranje objekta u programiranju naziva “klasa”. Klasa ima istu ulogu kao i šablon u realnom životu, ona definiše kako će da izgleda, koja svojstva i koje funkcionalnosti će da ima kreirani objekat. Objekat nastao na osnovu klase (‘šablona’) se naziva “instanca” klase.
Svi objekti kreirani istom klasom imaju ista svojstva i funkcionalnosti ali sa različitim vrednosti tih svojstava.
Objekat nastao na osnovu klase (“šablona”) za kreiranje vozila imao bi ista svojstva kao i svaki drugi objekat nastao od iste klase (“marka”, “tip motora”, “godište proizvodnje”), ali sa svojim vrednostima tih svojstava. Npr. instanca vozilo bi mogla imati vrednost “Renault 4” za svojastvo “marka”, za svojstvo “tip motora” vrednost “1.0 benzin” i za svojstvo “godište proizvodnje” vrednost “1986”. Na sledećem primeru klasa “Vozilo” ima dva svojstva: “markaVozila” i “godisteProizvodnje” i jednu metodu “vozi()”:
1 2 3 4 5 6 7 8 9 10 |
class Vozilo { constructor(markaVozila, godisteProizvodnje) { this.markaVozila = markaVozila; // Atribut klase this.godisteProizvodnje = godisteProizvodnje; // Atribut klase } vozi() { console.log(`Vozilo marke ${this.markaVozila} iz ${this.godisteProizvodnje}. godine vozi.`); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Vozilo { markaVozila: string; godisteProizvodnje: number; constructor(markaVozila: string, godisteProizvodnje: number) { this.markaVozila = markaVozila; this.godisteProizvodnje = godisteProizvodnje; } vozi(): void { console.log(`Vozilo marke ${this.markaVozila} iz ${this.godisteProizvodnje}. godine vozi.`); } } |
Kreiranje i korišćenje instance klase Vozilo u okviru nekog programa bi izgledalo ovako (isto i u verziji za JS i TS):
1 2 |
let mojAuto = new Vozilo('Toyota', 2020); mojAuto.vozi(); // Ispisuje: Vozilo marke Toyota iz 2020. godine vozi. |
U primeru “mojAuto” je objekat kreiran na osnovu klase “Vozilo”, i pri kreiranju objekta su mu setovane vrednosti za markaVozila (“Toyota”) i godisteProizvodnje (“2020”). Ovaj objekat čuva te dve informacije i ima dodatnu funkcionalnost sa kojom u konzoli može da ispiše “Vozilo marke Toyota iz 2020. godine vozi” kad god da pozovemo metodu “vozi()”.
OBJAŠNJENJE:
Specijalna JS metoda “constructor” se poziva samo jednom i to prilikom kreiranja nove instance klase. U našem primeru, constructor prihvata unos dve vrednosti i onda setuje te vrednosti svojstavima klase (“markaVozila” i “godisteProizvodnje”) pri samoom instanciranju objekta. Više o klasama pogledaj u članku “Klase u JS-u”
Privatno svojstava objekta
Često je potrebno da se zabrani pristup nekim svojstvima objekta izvan samog objekta, takva svojstva se nazivaju “privatna” svojstva i ona nisu dostupna izvan definisanog objekta, tj. ona su “skrivena” od spoljašnjeg koda.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Vozilo { #markaVozila; #godisteProizvodnje; constructor(markaVozila, godisteProizvodnje) { this.#markaVozila = markaVozila; this.#godisteProizvodnje = godisteProizvodnje; } vozi() { console.log(`Vozilo marke ${this.#markaVozila} iz ${this.#godisteProizvodnje}. godine vozi.`); } } let mojAuto = new Vozilo('Toyota', 2020); mojAuto.vozi(); // Ispisuje: Vozilo marke Toyota iz 2020. godine vozi. |
Ukoliko bi smo pokušali pri programiranju da napišemo:
1 |
mojAuto.#markaVozila = "Zastava"; |
Ovo će izazvati grešku u JavaScriptu (ES2020 i novijim).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Vozilo { private _markaVozila: string; private _godisteProizvodnje: number; constructor(markaVozila: string, godisteProizvodnje: number) { this._markaVozila = markaVozila; this._godisteProizvodnje = godisteProizvodnje; } vozi(): void { console.log(`Vozilo marke ${this._markaVozila} iz ${this._godisteProizvodnje}. godine vozi.`); } } let mojAuto = new Vozilo('Toyota', 2020); mojAuto.vozi(); // Ispisuje: Vozilo marke Toyota iz 2020. godine vozi. |
Ukoliko bi smo pokušali pri programiranju da napišemo:
1 |
mojAuto._markaVozila = "Zastava"; |
TypeScript kompajler će prijaviti grešku jer pokušavate da pristupite privatnom svojstvu klase izvan nje.
U prethodnom primeru svojstva “markaVozila” i “godisteProizvodnje” su privatna, što znači da ne mogu biti pristupljena ili promenjena direktno izvan instance klase. Metoda vozi i dalje može da pristupi ovim privatnim svojstvima, jer je ona deo klase. Ukoliko bi pokušali da pristupimo nekom privatnom svojstvu ili da mu promenimo ime onda bi kompajler izbacio grešku.
U JavaScriptu, koncept privatnih svojstava nije bio formalno podržan do nedavno (ES2020 je uveo privatna polja sa # prefiksom). Pre toga, konvencija je bila koristiti određene nazive (kao što je underscore “_” prefiks) kako bi se signaliziralo da su određena svojstva “privatna”. Međutim, ovo nije bila prava enkapsulacija na nivou jezika, već više konvencija unutar zajednice programera.
Enkapsulacija
OBJAŠNJENJE:
“Enkapsulacija” je jedan od principa objektno orjentisano programiranja i zasniva se na sakrivanju stanja svojstva klase za kod izvan same klase, omogućivajući kontrolisani pristup svojstvu samo preko metoda predefinisanih za to.
U prethodnom primeru je jasno zašto je neophodno da svojstva “markaVozila” i “godisteProizvodnje” budu privatna tj. da se setuju samo jednom pri kreiranju objekta bez načina da se naknadno promene izvan same klase. Medjutim nekada su potrebna privatna svojstva čije vrednosti mogu da se menjaju iz “spoljašnjeg koda”. U takvim slučajevima se to omogući ali samo kroz kontrolisan pristup, odličan primer bi bilo novo privatno svojstvo “bojaKaroserije”, čiju bi promenu dozvolili i van objekta samo kroz specijalizovanu metodu pod nazivom “farbanjeKola()”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class Vozilo { #markaVozila; #godisteProizvodnje; #bojaKaroserije; constructor(markaVozila, godisteProizvodnje, bojaKaroserije) { this.#markaVozila = markaVozila; this.#godisteProizvodnje = godisteProizvodnje; this.#bojaKaroserije = bojaKaroserije; } vozi() { console.log(`Vozilo marke ${this.#markaVozila} iz ${this.#godisteProizvodnje}. godine, boje ${this.#bojaKaroserije}, vozi.`); } get bojaKaroserije() { return this.#bojaKaroserije; } farbanjeKola(novaBoja) { this.#bojaKaroserije = novaBoja; } } let mojAuto = new Vozilo('Toyota', 2020, 'crvena'); console.log(mojAuto.bojaKaroserije); // Ispisuje: crvena mojAuto.farbanjeKola('plava'); console.log(mojAuto.bojaKaroserije); // Ispisuje: plava |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class Vozilo { private _markaVozila: string; private _godisteProizvodnje: number; private _bojaKaroserije: string; constructor(markaVozila: string, godisteProizvodnje: number, bojaKaroserije: string) { this._markaVozila = markaVozila; this._godisteProizvodnje = godisteProizvodnje; this._bojaKaroserije = bojaKaroserije; } vozi(): void { console.log(`Vozilo marke ${this._markaVozila} iz ${this._godisteProizvodnje}. godine, boje ${this._bojaKaroserije}, vozi.`); } get bojaKaroserije(): string { return this._bojaKaroserije; } farbanjeKola(novaBoja: string): void { this._bojaKaroserije = novaBoja; } } let mojAuto = new Vozilo('Toyota', 2020, 'crvena'); console.log(mojAuto.bojaKaroserije); // Ispisuje: crvena mojAuto.farbanjeKola('plava'); console.log(mojAuto.bojaKaroserije); // Ispisuje: plava |
Apstrakcija
“Apstrakcija” je još jedan od principa objektno orjentisano programiranja i podrazumeva fokusiranje na ono što je bitno i skrivanje kompleksnosti od korisnika (korisnik = “kod izvan klase”). Kada postoji takva potreba za nekom funkcionalnosti kojoj ne želimo da se pristupa izvan klase onda je potrebno da je definišemo kao “privatnu funkciju”.
U sledećem primeru ćemo dodati novo privatno svojstvo “količinaGoriva” čija vrednost može da se poveća samo kroz specijalizovanu metodu “napuniGorivo()”, i možemo da promenimo metodu “vozi()” i da joj damo novu funkcionalnost tako da će ona biti zadužena da smanji količinu goriva u rezervaru u zavisnosti koliko kilometara vožnja treba da predje. Ova metoda će od sada da prihvata jedan ulazni parametar “broj kilometara vožnje” te će na osnovu njega i vrednosti iz svojstva “potrošnjeGoriva” smanjivati količinu goriva u rezervaru po odgovarajućoj formuli. Takodje metoda treba proveriti da li ima dovoljno goriva za planiranu vožnju i ukoliko nema onda da ne izvrši vožnju i da u konzoli ispiše poruku upozorenje. Proveru da li ima goriva u rezervaru ćemo posvetiti privatnoj funkciji pod nazivom “imaLiDovoljnoGoriva()”. Pošto je ova metoda potrebna samo unutar klase i nije potrebno da njoj ima pristup neko spolja ona je odličan primer apstrakciju (skrivena kompleksnost) i privatnu funkciju.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
class Vozilo { #markaVozila; #godisteProizvodnje; #bojaKaroserije; #kolicinaGoriva = 0; #potrosnjaGoriva; // potrošnja goriva na 100km constructor(markaVozila, godisteProizvodnje, bojaKaroserije, potrosnjaGoriva) { this.#markaVozila = markaVozila; this.#godisteProizvodnje = godisteProizvodnje; this.#bojaKaroserije = bojaKaroserije; this.#potrosnjaGoriva = potrosnjaGoriva; } vozi(kilometri) { let potrebnoGorivo = kilometri * this.#potrosnjaGoriva / 100; if (this.#imaLiDovoljnoGoriva(potrebnoGorivo)) { this.#kolicinaGoriva -= potrebnoGorivo; console.log(`Vozilo marke ${this.#markaVozila} vozi.`); } else { console.error('Nema dovoljno goriva za ovu vožnju.'); } } #imaLiDovoljnoGoriva(potrebnoGorivo) { return this.#kolicinaGoriva >= potrebnoGorivo; } napuniGorivo(kolicina) { this.#kolicinaGoriva += kolicina; // Dodajemo gorivo na prethodno stanje } kolikoImaGoriva() { console.log(`Količina goriva: ${this.#kolicinaGoriva} litara.`); } get bojaKaroserije() { return this.#bojaKaroserije; } farbanjeKola(novaBoja) { this.#bojaKaroserije = novaBoja; } } let mojAuto = new Vozilo('Toyota', 2020, 'crvena', 6); mojAuto.napuniGorivo(50); // Dodaje 50 litara goriva mojAuto.kolikoImaGoriva(); // Ispisuje količinu goriva mojAuto.vozi(200); // Vozi i troši gorivo mojAuto.kolikoImaGoriva(); // Ispisuje preostalu količinu goriva |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
class Vozilo { private _markaVozila: string; private _godisteProizvodnje: number; private _bojaKaroserije: string; private _kolicinaGoriva: number = 0; private _potrosnjaGoriva: number; // potrošnja goriva na 100km constructor(markaVozila: string, godisteProizvodnje: number, bojaKaroserije: string, potrosnjaGoriva: number) { this._markaVozila = markaVozila; this._godisteProizvodnje = godisteProizvodnje; this._bojaKaroserije = bojaKaroserije; this._potrosnjaGoriva = potrosnjaGoriva; } vozi(kilometri: number): void { let potrebnoGorivo = kilometri * this._potrosnjaGoriva / 100; if (this._imaLiDovoljnoGoriva(potrebnoGorivo)) { this._kolicinaGoriva -= potrebnoGorivo; console.log(`Vozilo marke ${this._markaVozila} vozi.`); } else { console.error('Nema dovoljno goriva za ovu vožnju.'); } } private _imaLiDovoljnoGoriva(potrebnoGorivo: number): boolean { return this._kolicinaGoriva >= potrebnoGorivo; } napuniGorivo(kolicina: number): void { this._kolicinaGoriva += kolicina; // Dodajemo gorivo na prethodno stanje } kolikoImaGoriva(): void { console.log(`Količina goriva: ${this._kolicinaGoriva} litara.`); } get bojaKaroserije(): string { return this._bojaKaroserije; } farbanjeKola(novaBoja: string): void { this._bojaKaroserije = novaBoja; } } let mojAuto = new Vozilo('Toyota', 2020, 'crvena', 6); mojAuto.napuniGorivo(50); // Dodaje 50 litara goriva mojAuto.kolikoImaGoriva(); // Ispisuje količinu goriva mojAuto.vozi(200); // Vozi i troši gorivo mojAuto.kolikoImaGoriva(); // Ispisuje preostalu količinu goriva |
U klasi Vozilo, detalji o tome kako se računa potrošnja goriva ili kako se proverava da li ima dovoljno goriva su sakriveni unutar metode, pa kada korisnik klase poziva metodu vozi() on nema potrebu da zna detalje o tome kako se gorivo troši ili kako se vrši provera nivoa goriva.
Nasleđivanje
Objektno orjentisano programiranje omogućava da podklasa nasledi sve karateristike svoje bazne klase ali da pri tom i doda nove funkcionalnoti. U ovom primeru klasa “Formula” nasleđuje sve osobine i metode klase “Vozilo” i proširuje ih sa svojim novim svojstvom “_maksimalnaBrzina” i metodom “voziTrku()”. Metoda voziTrku() je slična metodi vozi() ali za razliku od nje ona pri trci troši dvostruko više goriva od normalne potrošnje (kada se poziva metoda vozi()).
NAPOMENA:
U okviru bazne klase metoda “imaLiDovoljnoGoriva()” je privatna (u TS-u sa ključnom reči private, a u JS-u sa prefiksom #). Da bismo omogućili korišćenje privatne metode promeniGorivo i unutar potklase, u TS-u je potrebno promeniti njeno stanje iz “private” u “protected”. Medjutim privatne metode i svojstva u JavaScriptu označene sa # su dostupne samo unutar klase u kojoj su definisane i ne postoji direktan način da ih učinite dostupnim u izvedenim (extended) klasama (kao što je to moguće u TypeScriptu sa protected). Da bi smo to uradili i u JS-u potrebno je umesto prave privatnosti (metode označene sa prefiksom “#””), koristiti samo konvenciju imenovanja sa prefiksom “_”, čime metode označuje da je “privatne”, ali one tehnički ostaju javno dostupne.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Formula extends Vozilo { constructor(markaVozila, godisteProizvodnje, bojaKaroserije, potrosnjaGoriva, maksimalnaBrzina) { super(markaVozila, godisteProizvodnje, bojaKaroserije, potrosnjaGoriva); this._maksimalnaBrzina = maksimalnaBrzina; } voziTrku(kilometri) { const potrosnjaZaTrku = this._potrosnjaGoriva * 2; // Duplo veća potrošnja if (this._imaLiDovoljnoGoriva(-kilometri * potrosnjaZaTrku / 100)) { this._kolicinaGoriva -= kilometri * potrosnjaZaTrku / 100; console.log(`Formula marke ${this._markaVozila} vozi trku.`); } else { console.error('Nema dovoljno goriva za trku.'); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Formula extends Vozilo { private _maksimalnaBrzina: number; constructor(markaVozila: string, godisteProizvodnje: number, bojaKaroserije: string, potrosnjaGoriva: number, maksimalnaBrzina: number) { super(markaVozila, godisteProizvodnje, bojaKaroserije, potrosnjaGoriva); this._maksimalnaBrzina = maksimalnaBrzina; } voziTrku(kilometri: number): void { const potrebnoGorivo = kilometri * this._potrosnjaGoriva * 2 / 100; // Duplo veća potrošnja if (this._imaLiDovoljnoGoriva(potrebnoGorivo)) { this._kolicinaGoriva -= potrebnoGorivo; console.log(`Formula marke ${this._markaVozila} vozi trku sa maksimalnom brzinom od ${this._maksimalnaBrzina} km/h.`); } else { console.error('Nema dovoljno goriva za trku.'); } } } |
Nova ekstendovana klasa bi mogla da se koristi u programu na sledeći način:
1 2 3 4 |
let mojaFormula = new Formula('Ferrari', 2022, 'crvena', 10, 350); mojaFormula.napuniGorivo(100); // Dodaje 100 litara goriva mojaFormula.voziTrku(10); // Ispisuje drugačiju poruku i duplo više troši gorivo u odnosu na običnu vožnju mojaFormula.kolikoImaGoriva(); // Ispisuje preostalu količinu goriva |
Polimorifizam
Objektno orjentisano programiranje podržava još jedan koncept “Polimorfizam” koji predstavlja sposobnost koda da se ponaša na različite načine, u zavisnosti od objekta sa kojim radi. Ovo je posebno korisno u situacijama kada želimo da različite klase imaju isti naziv za metodu, ali svaka klasa na svoj način implementira tu metodu. Postoje dva osnovna tipa polimorfizma: statički (ili compile-time) “Overloading” i dinamički (ili run-time) “Override”.
Dinamički Polimorfizam (Override)
Odličan primer za dinamički Polimorfizam bi bila metoda vozi(), gde pri pozivanju metode štampamo drugačije poruke u zavisnosti od klase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Vozilo { // ... (ostali delovi klase ostaju isti) vozi(kilometri: number): void { let potrebnoGorivo = kilometri * this._potrosnjaGoriva / 100; if (this._imaLiDovoljnoGoriva(potrebnoGorivo)) { this._kolicinaGoriva -= potrebnoGorivo; console.log(`Vozilo marke ${this._markaVozila} vozi.`); } else { console.error('Nema dovoljno goriva za ovu vožnju.'); } } // ... (ostali delovi klase ostaju isti) } |
A proširena klasa Formula bi sada izgledala ovako:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Formula extends Vozilo { // ... (ostali delovi klase ostaju isti) vozi(kilometri: number): void { let potrebnoGorivo = kilometri * this._potrosnjaGoriva * / 100; if (this._imaLiDovoljnoGoriva(potrebnoGorivo)) { this._kolicinaGoriva -= potrebnoGorivo; console.log(`Formula ${this._markaVozila} vozi sporo kao po jajima!`); } else { console.error('Nema dovoljno goriva!.'); } } // ... (ostali delovi klase ostaju isti) } |
Demonstracija polimorfizma:
1 2 3 4 5 6 7 |
let obicniAuto = new Vozilo('Toyota', 2020, 'plava', 7); obicniAuto.napuniGorivo(50); obicniAuto.vozi(10); // Ispisuje standardnu poruku o vožnji let formulaAuto = new Formula('Ferrari', 2021, 'crvena', 10, 350); formulaAuto.napuniGorivo(50); formulaAuto.vozi(10); // Ispisuje posebnu poruku o stilu vožnje |
U prethodnom primeru je prikazan Dinamički Polimorfizam (Override) pri kojem je u extendovanoj klasi “pregažena” funkcionalnost metode. Kompajler zna u toku izvršenja programa (runtime), na osnovu tipa objekta (“Vozilo” ili “Formula”) koja metoda treba da se pozove!
Statički Polimorfizam (Overloading)
Ovaj tip polimorfizma se dešava kada imamo više metoda unutar iste klase sa istim imenom, ali sa različitim parametrima (brojem, tipom, itd.). Kompajler odlučuje koju verziju metode da koristi na osnovu argumenata koje metoda prima prilikom poziva. U programski jezicima “Java” i “C#” je omogućen stvarni overloading metoda unutar klase a to podrazumeva da u okviru klase možete imati više metoda sa istim imenom, ali sa različitim potpisima (različiti tipovi i/ili broj parametara). Svaka od ovih metoda može imati svoju jedinstvenu implementaciju. Evo primera za overLoading u C#-u:
Primer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Kalkulator { public int saberi(int a, int b) { return a + b; } public int saberi(int a, int b, int c) { return a + b + c; } } public class Main { public static void main(String[] args) { Kalkulator kalkulator = new Kalkulator(); System.out.println(kalkulator.saberi(2, 3)); // Poziva metodu sa int parametrima System.out.println(kalkulator.saberi(1, 2, 3)); // Poziva metodu sa tri int parametra } } |
U TypeScriptu, overloading (preopterećenje) metoda se postiže definisanjem više potpisa za istu funkciju, ali sa jednom implementacijom koja mora biti kompatibilna sa svim potpisima. Evo primera sličnog kalkulatora kao u C# primeru, ali prilagođenog za TypeScript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Kalkulator { // Definisanje potpisa funkcije saberi(a: number, b: number): number; saberi(a: number, b: number, c: number): number; // Implementacija koja zadovoljava sve potpise saberi(a: number, b: number, c?: number): number { if (c !== undefined) { return a + b + c; } return a + b; } } let kalkulator = new Kalkulator(); console.log(kalkulator.saberi(2, 3)); // Poziva metodu sa dva parametra console.log(kalkulator.saberi(1, 2, 3)); // Poziva metodu sa tri parametra |
U ovom TypeScript primeru, klasa Kalkulator ima dva potpisa za metodu saberi: jedan sa dva parametra i drugi sa tri parametra. Postoji samo jedna implementacija metode saberi koja koristi opcionalni parametar (označen sa ?) kako bi se omogućilo pozivanje metode sa dva ili tri argumenta. Na ovaj način, TypeScript omogućava overloading uz očuvanje tipičnih karakteristika statičke provere tipova koje pruža jezik.
Primer
U ovome primeru ista metoda može da primi i “različit tip parametara”, pa u C# to izgledalo ovako:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class Kalkulator { public int Saberi(int a, int b) { return a + b; } public int Saberi(int a, int b, int c) { return a + b + c; } public string Saberi(string a, string b) { return a + b; } } class Program { static void Main(string[] args) { Kalkulator kalkulator = new Kalkulator(); Console.WriteLine(kalkulator.Saberi(2, 3)); // 5 Console.WriteLine(kalkulator.Saberi(1, 2, 3)); // 6 Console.WriteLine(kalkulator.Saberi("hello", "world")); // "helloworld" } } |
Kako TypeScript ne podržava više stvarnih implementacija istog naziva funkcije (kao što to čine Java i C#…), moramo koristiti tipove unije i proveru tipova unutar jedne implementacije da biste obradili različite slučajeve. Evo kako bi to moglo izgledati:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Kalkulator { // Definisanje potpisa funkcije saberi(a: number, b: number): number; saberi(a: number, b: number, c: number): number; saberi(a: string, b: string): string; // Implementacija koja zadovoljava sve potpise saberi(a: number | string, b: number | string, c?: number): number | string { if (typeof a === 'string' || typeof b === 'string') { return a.toString() + b.toString(); } else { return c !== undefined ? a + b + c : a + b; } } } let kalkulator = new Kalkulator(); console.log(kalkulator.saberi(2, 3)); // 5 console.log(kalkulator.saberi(1, 2, 3)); // 6 console.log(kalkulator.saberi("hello", "world")); // "helloworld" |
U ovoj verziji, saberi metoda može prihvatiti ili brojeve ili stringove. Funkcija unutar klase koristi proveru tipova (typeof) da odredi da li su argumenti brojevi ili stringovi i obavlja odgovarajuću operaciju (sabiranje brojeva ili spajanje stringova) na osnovu toga.
Ovo je fleksibilan način da se u TypeScriptu upravlja različitim vrstama parametara unutar jedne funkcije, iako se mora pažljivo rukovati tipovima podataka kako bi se izbegle greške u vreme izvršenja.