neljapäev, 8. juuli 2010

JavaScripti moodulite vaheline suhtlus JavaScriptis

Tänapäevased keerulisemad veebilehed koosnevad tõenäoliselt mitmetest eri komponentidest, mida suuremal või vähemal määral manatakse lehele JavaScripti abil. Nendeks komponentideks võivad muu hulgas olla näiteks järgmised elemendid - sisselogimise vorm mis avaneb hiirekliki peale (algselt on näha ainult tekst "Logi sisse"); bännerite kuvamine lehel; pisike interaktiivne kast ilmateateinfoga (saab valida piirkonda, aega jne); lightbox efekt piltide kuvamiseks; valuuta konverteerimise vidin jms.

Kuigi eemalt vaadates pole neil komponentidel palju ühist, võib siiski juhtuda olukordi kus eri komponendid vajavad võimalust üksteisega suhtlemiseks, et selle abil enese tööd parendada. Näiteks kui kasutaja valib ilmateate kastis mõne muu asukoha, kui algselt määratud, tahaks bännerite kuvaja võibolla näidata kasutajale ainult neid bännereid, mis on valitud piirkonnaga seotud. Või kui kasutaja passiivsuse tõttu keskkonnast automaatselt välja logitakse, ilma uut lehenäitamist tegemata, tahaksid tõenäoliselt kõik komponendid sellest teada saada, nii et suudaksid endid sobivalt ümber häälestada. Rääkimata siis veel sellistest juhtudest, kus seosed on palju otsesemad - kui kasutaja end välja logib/välja logitakse tuleks taastada vahepeal eemaldatud sisselogimise link.

Tõenäoliselt tegelevad kõikide nende operatsioonidega erinevad moodulid. Sisselogimisega üks, bännerite kuvamisega teine ja ilmateatega hoopis kolmas. Seega tuleb välja mõelda mingi mõistlik viis, kuidas need eraldiseisvad moodulid üksteisega suheldud saaks.

Naabrite eiramine

Kõige triviaalsem on lihtsalt teist moodulit eirata ja selle tegevused ise läbi viia. Näiteks kui ilmateate kastis on element <a id="tervitus_tekst"> mis sisseloginud kasutajale teatab "Tere [nimi]!" ning väljaloginud kasutajale "Tere kasutaja!", siis kutsudes esile väljalogimisprotsessi klikkides lehel lingil "Logi välja", ei anna kasutaja haldamise moodul sellest väljalogimisest ilmateate moodulile teada, vaid lihtsalt kirjutab selles oleva kasutajainfoga seotud elemendi üle.

Kasutaja logib välja ->
kasutajate moodul:
$("tervitus_tekst").innerHTML="Tere kasutaja!"

Antud lähenemine on võibolla lihtsustamise mõttes mõistlik väga väiksemahuliste, kuid kindlasti mitte keerulisemate elementide korral. Esiteks võib selline käitumine teise mooduli katki teha - too eeldab, et lehel on üks asi, tegelikult on aga hoopis midagi muud. Samuti võib kõik katki minna, kui lehel polegi ilmateate moodulit. Pealegi lõhutakse nii päris korralikult modulaarsust, kus iga komponent peaks olema eraldiseisev tervik ning pärsitakse ka skaleeruvust programmikoodi mõistes - taoline tegevus on alati erand ning kui erandid kipuvad kuhjuma, on oodata suuri probleeme.

Tagasikutse funktsioonide kasutamine

Keerulisem viis oleks seada üles tagasikutsefunktsioonide registreerimise süsteem. Sellisel juhul ilmateate moodul ütleks lehe laadimisel, et erinevate sisselogimisega seotud sündmuste korral tuleks käivitada parameetrina antud tagasikutsefunktsioon. Juhul kui nüüd kasutaja välja logib, võtab kasutajate haldamise moodul ette kõik sedasi registreeritud tagasikutsefunktsioonid ja paneb need ükshaaval käima. Nii saab ilmateate moodul teada, et kasutaja on välja loginud ja asendab ise tervituse teksti vastavaga.

Tagasikutse funktsiooni registreerimine ->
ilmateate moodul:
kasutajate_moodul.notify_logout_callback = function(){
    ilmateate_moodul.kasutaja_logis_välja();
};

Kasutaja logis välja ->
kasutajate moodul:
kasutajate_moodul.notify_logout_callback();

Antud lähenemine on juba tunduvalt parem kui eelmisena näidatud variant - seekord on moodulid üksteisest juba üsna eraldatud, kumbki teise tööpõllule ei roni. Samuti ei teki ka probleeme, kui ilmateate moodul lehelt üldse puudu on. Küll aga tekivad probleemid siis, kui lehel puudub sisselogimise moodul. Moodulid on endiselt üksteisest kaudselt sõltuvad, kuna vähemalt üks eeldab teise olemasolu (ilmateate moodul eeldab kasutajate mooduli olemasolu).

Kohandatud sündmuste kasutamine

Nõrgalt seotud elementide korral tasuks kõige rohke kaaluda just kohandatud sündmuste kasutusele võttu. Süsteem oleks sarnane nagu tavaliste tagasikutse funktsioonide kasutamise korralgi - ilmateate moodul registreerib tagasikutse funktsiooni, mis käivitatakse juhul kui kasutaja haldamise moodul on kasutaja välja loginud, kuid seekord ei registreeritaks seda sündmust mitte otse kasutajate haldaja pihta, vaid hoopis kohandatud sündmusena mõne suvalise DOM elemendi, näiteks document pihta.

Kui nüüd kasutaja välja logib, annab kasutajate haldamise moodul sellest teada, aga mitte otse ilmateate moodulile, vaid hoopis sellele sõnumikandjana kasutatavale elemendile. Too võtab ükshaaval ette kõik huvitatud ja käivitab nende tagasikutse funktsioonid.

Kohandatud sündmuse registreerimine ->
ilmateate moodul:
document.observe("kasutaja:logis-välja", function(event){
    ilmateate_moodul.kasutaja_logis_välja();
});

Kasutaja logis välja ->
kasutajate moodul:
document.fire("kasutaja:logis-välja");

Viimane lähenemine on käsitletutest kõige turvalisem - kõik moodulid on üksteisest täielikult eraldatud. Kuna üksteisega suhtlemiseks kasutatakse vahendajat, ei puutu ükski moodul otseselt teisega kunagi kokku. Nii võib suvaline moodul heita üles teate, et nüüd midagi juhtus ja juhul kui mõnda teist moodulit see huvitama peaks, saab ta selle teate kätte. Kui aga selliseid huvitatuid pole, ei lähe ka midagi katki. Samuti kui mõni moodul määrab ära teda huvitava sündmuse, siis ei juhtu ka midagi sellest, kui ühtegi taolist sündmust esilekutsuvat moodulit lehel polegi.

Täiendavaks plussiks kohandatud sündmuste kasutamise korral oleks veel "tasuta kaasa tulevad" sündmuste haldamise võimalused - iga käivitatud tagasikutse funktsioon saab standardsel viisil edasist sündmuse täitmist peatada (event.stopPropagation), saab tegevust ka maha registreerida, nii et seda järgmiste sündmuste juures ei kasutata (document.stopObserving) jne.

Vaata ka seda: Ajaxian, How Custom Events Will Save Us All

NB! Näidetes on kasutatud Prototype käsklusi, sama on võimalik saavutada ka kõigi teiste sarnaste teekide abil.

Kommentaare ei ole: