Robotžmogiams: dekoratoriai programavime

Jau turbūt kokį pusmetį programuodamas šen bei ten panaudoju Python’o dekoratorius. Tai apgaubiančios funkcijos, kurios modifikuoja kitų funkcijų ar metodų rezultatus. Vos prieš keletą dienų, aprimus darbinei įtampai, žvilgtelėjau į dekoratorių vidų ir pasiaiškinau, kaip jie veikia.

Python’e dekoratoriai naudojami taip:

from kokia.nors.biblioteka import is_sparciosios_atmintines

def gauk_atsakyma(argumentas1, argumentas2):
    # kažkokie skaičiavimai
    return atsakymas
gauk_atsakyma = is_sparciosios_atmintines(gauk_atsakyma)

arba taip:

from kokia.nors.biblioteka import is_sparciosios_atmintines

@is_sparciosios_atmintines
def gauk_atsakyma(argumentas1, argumentas2):
    # kažkokie skaičiavimai
    return atsakymas

Kaip tikriausiai susipratai, čia is_sparciosios_atmintines yra funkcijos gauk_atsakyma dekoratorius.

Nors tokio tipo dekoratorius yra Python’o išmislas, pati dekoratoriaus koncepcija turėtų veikti daugumoj interpretuojamų kalbų (PHP, Java, ActionScript ar kt.).

Dekoratoriaus vidurius pailiustruosiu JavaScript’u.

function dekoruok(dekoruojamaFunkcija) {
    function apgaubk() {
        // surenkam visus parėjusius argumentus į vieną masyvą
        var argumentai = [];
        for (i=0, len=arguments.length; i<len; i++)
            argumentai.push(arguments[i]);
        // -- čia galima modifikuoti argumentus --
        rezultatas = dekoruojamaFunkcija.apply(this, argumentai);
        // -- čia galima modifikuoti rezultatą --
        return rezultatas
    }
    return apgaubk;
}

function daryk_ka_nors(a, b, c) {
    // ...
}
daryk_ka_nors = dekoruok(daryk_ka_nors);

Viskas vyksta taip: dekoratorius priima dekoruojamąją funkciją kaip argumentą ir grąžina naują funkciją, kurioje pasirinktinai apdoroja dekoruojamosios argumentus, iškviečia pačią dekoruojamąją funkciją (nebūtinai vienąkart), pasirinktinai apdoroja jos rezultatą ir galiausiai jį grąžina.

Ilgai negalvojus, į smegeninę atėjo mintis, kur dekoratorių panaudot JavaScript programavime praktiškai. Tarkim, žiniatinklio programoje (angl. web application) būtų daugybė nuorodų, virš kurių spustelėjus, turi būti įvykdyti tam tikri skaičiavimai ar AJAX užklausos pagal verslo reikalavimus. Be kita ko, kiekvienai nuorodai turi būti atjungtas standartinis nuorodos veikimas – siuntimas į puslapį su funkcijai alternatyvia informacija ar forma, per kurią skaičiavimas ar belekas galėtų būti įvykdytas ir be JavaScript pagalbos. Taigi minėtas standartinis nuorodos veikimas gali būti atjungtas kiekvieną verslo logikos funkciją apgaubiant dekoratoriumi.

function cancelEvent(decoratedFunction) {
    /**
    Dekoratorius, atšaukiantis standartinį įvykio veikimą bei
    visų tėvinių elementų įvykių apdorojimą
    */
    function wrap() {
        var args = [];
        for (i=0, len=arguments.length; i<len; i++) {
            args.push(arguments[i]);
        }
        e = args.shift()
        if (!e) var e = window.event;
        e.cancelBubble = true;
        e.returnValue = false;
        if (e.stopPropagation) e.stopPropagation();
        if (e.preventDefault) e.preventDefault();
        return decoratedFunction.apply(this, args);
    }
    return wrap;
}

function test(key, value) {
    /**
    "Svarbi" verslo logikos funkcija, grąžinanti "svarbų" rezultatą.
    Viskas čia labai rimta.
    */
    return key + " = " + value;
}
// Dekoruojam "svarbią" funkciją.  Rezultatas tebūnie
// pavadintas kitu vardu, kad vis dar būtų įmanoma kreiptis ir į
// originalią funkciją.
var test_from_link = cancelEvent(test);

window.onload = function() {
    // pasikrovus puslapiui, susirandam nuorodą su ID "test"
    // ir priskiriam jai įvykį onclick apdorojančią funkciją, kuri
    // kvies mūsų dekoruotąją test
    document.getElementById("test").onclick = function(e) {
        alert(test_from_link(e, 'vardas', 'Aidas'));
    }
}

Beje šitame pavyzdyje darau prielaidą, kad puslapio HTML kode nėra jokių onclick atributų – tik semantiškas turinys, o JavaScript kaip ir CSS patalpinti atskiruose failuose.

Serveriniame interneto programavime dekoratoriai gali būti naudojami apriboti tam tikrų funkcijų vykdymą tik tiems lankytojams, kurie turi atitinkamas teises, kešuoti sudėtingų skaičiavimų ar teksto apdorojimo rezultatus ir tuo pačiu paimti kešuotą turinį iš sparčiosios atmintinės bei kitose situacijose (kas turit gerų idėjų – mestelkit komentaruose).

Tai tiek apie dekoratorius. Einu įsipilt tepalo.

Tags:

If you liked this entry, you may also like:

Leave a Reply