Patterns in JS
Kristofer Aleksander je dao sledeću definiciju paterna: „Svaki patern je trodelno pravio, koje uspostavlja relaciju izmedju izmedju nekog problema, njegovog rešenja i njihovog konteksta. Patern je u isto vreme i stvar koja se dešava u stvarnosti, i pravilo koje govori kada i kako se kreira navedena stvar.“
Paterni predstavljaju gotova rešenja koja nalaze primenu prilikom projektovanja i implementacije softvera. Paterni projektovanja su paterni koji su nezavisni od bilo kakve implemntacione tehnologije. Dakle paterni predstavljaju opšta, apstraktna rešenja odredjenih problema koja se javljaju prilikom projektovanja objektno-orijentisanog softvera i koja su predložena rešavanju konkretnih problema. Odredjeni patern je definisan sa imenom, problemom, rešenjem i posledicama. Ime se upotrebljava za opis nekog paterna u nekoliko kratkih rečenica. Problem opisuje situaciju i kontekst prilikom kojeg se patern koristi. Rešenje opisuje opšte odnose medju elementima koji uzimaju učešća u rešavanju problema. Posledice opisuju rezultate primene paterna, ali takodje i opisa opravdanosti upotrebe paterna. Rešenja iz paterna se mogu ponovo koristiti.
Paterni su proverena rešenja, rešenja iz paterna se mogu ponovo koristiti što je i cilj paterna i paterni su pogodni jer imaju jasno vidljivu strukturu problema i rešenja. Paterni projektovanja se dele na kreacione paterne, strukturne paterne i paterne ponašanja. Kreacioni paterni su usesredjeni na kreiranje objekata koji su kreirani prikladnu za situaciju u kojoj se nalazimo (Constructor, Factory, Abstract, Prototype, Singleton i Builder). Strukturni paterni se odnose na strukturu svakog objekta i pronalaženje načina za ostvarivanje veze izmedju njih (Decorator, Facade, Flyweight, Adapter, Proxy itd.). Paterni ponašanje se odnose na unapredjenje komunikacije izmedju objekata (Iterator, Mediator, Observer, Visitor itd.).
Kratak prikaz Javascript „klasa“
Koncept paterna je blisko povezan sa konceptom klasa u objektno orijentisanom razvoju softvera. Najpopularniji pristup jeste definisanjem funkcija i kreiranje objekata korišćenjem ključne reči new uz naziv funkcije. Ključna reč this se može koristiti u okviru funkcija za definisanje osobina i metoda za objekte. U narednom delu je prikazana funkcija Drzava koja ima ulogu klase, zatim njeno „instanciranje“ i pozivanje metoda.
function Drzava(brojstanovnika) {
this.naziv = "Srbija";
this.glavnigrad = "Beograd";
this.povrsina = 88361;
this.brojstanovnika = brojstanovnika;
this.prikazi = function() {
return this.nastavnik + " " + this.glavnigrad + " " + this.povrsina + " km na kvadrat" + this.brojstanovnika;
}
}
var srb = new Drzava(7164824);
document.write(srb.prikazi());
Rezultat izvršenja navedenog progama možemo pogledati na sledećoj slici.
Builder patern
Kada radimo sa DOM elementima, često želimo da kreiramo te elemente dinamički a ne odmah na početku. Ovaj proces se može znatno usložniti povećanjem kompleksnosti aplikacije. Builder patern omogućava kreiranje objekata samo specifirajući sadržaj nekog objekata., nezavisno od toga kako se objekat kreira, razdvajajući kreiranje od reprezentacije nekog objekta. Mi dakle definišemo strukturu nekog objekta bez potrebe za kreiranjem samog objekta. Naredni primer prikazuje primer korišćenja Builder paterna.
var Builder = function() {
var korisnickoIme = "nenad";
var sifra = "nenad";
return {
promenaKorisnickogImena : function(korIme) {
korisnickoIme = korIme;
return this;
},
promenaSifre : function(pass) {
sifra = pass;
return this;
},
build : function() {
return "Korisnicko ime je: " + korisnickoIme +", Sifra je: " + sifra;
}
};
};
var builder = new Builder();
console.log(builder.build());
var promena1 = builder.promenaKorisnickogImena("pejovic").promenaSifre("pejovic").build();
var promena2 = builder.promenaSifre("admin").build();
Predhodni primer prikazuje primer “klase” Builder koja ima metodu build, promenaKorisnickogImena i promenaSifre. Pozivanjem metode build ispisuje se vrednosti polja korisničko ime i šifra koja imaju svoju podrazumevanu vrednost. Pozivanjem odredjenih metoda menjaju se ova polja vrednostima prosledjenim kroz parametre te ponovno pozivanje metode build daje različite rezultate sa novom šifrom i korisničkim imenom.
Singleton patern
Ovaj patern obezbedjuje klasi jedinstveno pojavljivanje i jedinstven globalni pristup do tog jedinstvenog pojavljivanja. Jedinstvena instanca se naziva singleton. Ovaj patern je pogodan u situacijama kada neke sistemske akcije moraju biti koordinisane sa jednog mesta. Primer je klasa koja je zadužena sa ostvarivanje konekcije prema bazi podataka. Sledeći primer prikazuje implementaciju Singleton paterna u Javascript programskom jeziku.
var Singleton = (function() {
var instanca;
function inicijalizacija() {
function metoda1() {
alert("Singleton patern!");
}
function metoda2() {
alert("Kreacioni paterni!");
}
return {
prikaziNazivPaterna: metoda1,
prikaziKategorijuPaterna: metoda2
};
}
function vratiInstancu() {
if (!instanca) {
instance = inicijalizacija();
} else {
return instanca;
}
}
return {
vratiInstancu: vratiInstancu;
}
})();
Singelton.vratiInstancu.prikaziNazivPaterna();
Navedeni primer prikazuje klasu Singleton koja ima ima metodu vratiInstancu i polje instanca. Ova metoda, ukoliko je polje instanca nedefinisano, poziva u sebi metodu inicijalizuje i povratnu vrednost ove metode stavlja u promenljivu instanca. Povratna vrednost metode inicijalizuj je objekat sa dve definisane metode.
Decorator patern
Decorator patern produžuje dodatne odgovornosti do objekta dinamički. Ovaj patern obezbedjuje fleksibilnost u izboru klasa koje proširuju funkcionalnost. Klase koje pružaju funkcionalnost dekoracije nisu esencijalne sa tačke gledišta sistema. U našem javascript primer umesto dodavanja podklase, dodavaćemo polja i metode već kreiranim objektima.
function Plata() {
this.osnovnaPlata = function(){
return 15000;
};
}
function bonusi(plata) {
var op = plata.osnovnaPlata();
plata.osnovnaPlata = function() {
return op + 5000;
};
}
function topliObrok(plata) {
var op = plata.osnovnaPlata();
plata.osnovnaPlata = function() {
return op + 2000;
};
}
var plata = new Plata();
bonusi(plata);
topliObrok(plata);
console.log(plata.osnovnaPlata());
U ovom primeru je izvšen override objekta osnovnaPlata super-objekta Plata u funkcijama u kojima smo dodavali bonuse i topli obrok na vrednost osnovne plate.
Adapter patern
Adapter patern konvertuje interfejs neke klase ili objekta u drugi interfejs koji klijent očekuje. Adapter patern omogućuje zajednički rad klasama i interfejsima koji inače ne bi mogli da funkcionišu zajedno zbog nekompatibilnih interfejsa. Adapter patern uzima objekat koji smo kreirali, i umotava ga u drugi objekat koji odgovara nekom trećem objektu sa kojim prvi objekat treba da komunicira.
function pretraga() {
this.pronadjiKorisnika = function(username, pass) {
return "Nenad Pejovic"
}
}
function naprednaPretraga() {
this.postaviUsername(username) {};
this.postaviPassword(password) {};
this.finalnaPretraga() {
return "Nenad Pejovic";
}
}
function Adapter(username, password) {
var pretraga = naprednaPretraga;
return {
pronadjiKorisnika: function(username, password) {
pretraga.postaviPassword(password);
pretraga.postaviUsername(username);
return pretaga.pronadjiKorisnika();
}
}
}
function Client() {
var pretraga = new pretraga();
var username = "admin";
var username = "admin";
var adapter = new Adapter(username, password);
var pr = pretraga.pronadjiKorisnika(username, password);
var naprPr = adapter.pronadjiKorisnika(username, password);
}
Adapter prilagodjava objekat pretraga objektu naprednaPretraga
Chain of responsibility pattern
Izbegava čvrsto povezivanje izmedju pošiljaoca zahteva i njegovog primaoca, obezbedjuje lanac povezanih objekata, koji će da obradjuje zahtev sve dok se on ne obradi. Klijent objekat pokreće zahtev za obradom, dok handler objekti obradjuju zahteve. Sledeći primer prikazuje primer korišćenja Chain of responsibility paterna na primeru automata koji vraća kusur u apoenima za 20, 10, 5, 2 i 1.
var vracanjeKusura = function(kusur) {
this.kusur = kusur;
}
vracanjeKusura.prototype = {
vratiApoene: function(apoen) {
var apoeni = Math.floor(this.kusur / apone);
this.kusur -= apoeni * apoen;
console.log(apoeni);
return this;
}
}
};
function Klijent() {
var vracanjeKusura = new vracanjeKusura(214);
}
vracanjeKusura.vratiApoene(20).vratiApoene(10).vratiApoene(5).vratiApoene(5).vratiApoene(1);
}
State patern
State patern dopušta objektu da promeni ponašanje kada se menja njegovo interno stanje. Svaki objekat predstavlja specifično stanje.
var ukljuci = function(){
var ukljucen = "ne";
};
promeniKanal.prototype = function(){
if(this.ukljucen === "ne"){
this.ukljucen = "da";
} else{
this.ukljucen = "ne"
}
}
Ovaj primer prikazuje promenu stanja nekog uredjaja koja u zavisnosti od toga da li je uredjaj uključen ili ne, vrši se promena njegovog stanja.