AOP vs funktsioonid

Aspektorienteeritud programmeerimine (AOP) on üsna populaarne. Selle motivatsioon on vastavas Vikipeedia artiklis hästi lahti seletatud.

AOP on suurepärane tööriist selliste tõeliselt globaalsete kontseptsioonide jaoks nagu logimine, mis ei mõjuta otseselt koodi loogikat. Kuid AOP-i probleemid ilmnevad siis, kui seda kasutatakse ettevõtlusega seotud asjade jaoks, näiteks autoriseerimiseks. Need rakenduse aspektid peavad olema vastavas koodis selgelt nähtavad, et arendaja saaks vastava lähtekoodi lugemisel kohe näha, kas neid rakendatakse õigesti. AOP-põhised raamistikud lahendavad selle tavaliselt meetodi märkuste abil:

@RequireRole (Role.Admin) // selgelt nähtav külg
lõbus värskendusKasutajate õigused (…) {
    // loogika siin
}

Kuid loetavuse seisukohast ei erine see palju funktsionaalsest lähenemisest samale probleemile, kasutades funktsiooni NõuRole @RequireRole märkuse asemel:

lõbus värskendusKasutajate õigused (…) {
    needRole (Role.Admin) // viskab välja SecurityException
    // loogika siin
}

Lisaks on funktsionaalse lähenemise eeliseks see, et see suurendab ka keerukamaid õiguste kontrolle, näiteks meetodi parameetrite analüüsimist enne, kui otsustatakse, milline kasutajaroll on vajalik.

Sama kehtib ka muude aspektide, näiteks tehingute kohta. Kahjuks on keerukate mõistete funktsionaalne esitamine Java-s tülikas ja ebamugav, mis loob Java ökosüsteemis AOP-raamistike kunstliku populaarsuse.

Kotliniga see siiski nii pole. Kotlinis Java-laadse lähenemisviisi asemel AOP-ga tehtavatele tehingutele ja sellistele märkustele:

@Transactional
lõbus värskendusKasutajate õigused (…) {
    // loogika siin
}

Funktsionaalsel viisil ümber kirjutades on see sama loetav ja puhas:

lõbus updateUserPermissions (…) = tehinguline {
    // loogika siin
}

Selle funktsionaalse lähenemise eeliseks on see, et saate alati Ctrl / Cmd + klõpsata IDE-s tehingufunktsiooni deklaratsioonil ja kohe näha, mida see täpselt teeb, mis pole tavaliselt ühegi tavaliselt kasutatava AOP-raamistiku puhul võimalik. Isegi kui IDE pistikprogrammiga pakutakse aspekti lähtekoodile navigeerimist, nõuab selle loogika dešifreerimine teadmist eraldi rikkalikust API-st ja / või tavadest.

Kahjuks ei tähenda see Kotlini annotatsioonipõhise AOP funktsionaalne asendamine koheselt juhtumit, kui lokkide traksidega samale funktsioonile rakendatakse mitut aspekti ja lohud hakkavad kuhjuma:

lõbus updateUserPermissions (…) = logitud {
    tehinguline {
        // loogika siin
    }
}

Töö ümber on mõeldud kombineeritud kõrgema astme funktsiooni loomine, et hoida mitmete aspektide kasutusala puhtana:

lõbus updateUserPermissions (…) = loggedTransactional {
    // loogika siin
}

Funktsionaalse lähenemise veel üks puudus on see, et sellised aspektid nagu logimine vajavad juurdepääsu meetodi parameetritele. Need on tavaliselt spetsiaalsete API-de kaudu otse saadaval traditsioonilistes AOP-raamistikes, kuid Kotlini põhifunktsioonid ei pääse neile hõlpsalt juurde. Niisiis, selleks, et reaalses elus metsaraie aspekti puhtalt funktsionaalsel viisil kajastada, tuleb ikkagi kirjutada arvestatav kogus katlamaja koodi:

lõbus updateUserPermissions (parameetrid: Params) =
    logitud ("updateUserPermissions ($ params)")) {
        // loogika siin
    }

See kaalutlus teeb AOP-ist endiselt logimiseks valitud tööriista, kui teil seda tõesti rakenduses ülemaailmselt ja järjepidevalt vaja on, kuid usun, et AOP-i kasutamine sellistes aspektides nagu autoriseerimine ja tehingud on kuritarvitamine, arvestades Kotlinis saadaolevaid rikkalikke funktsionaalseid abstraktsioone. . Funktsioonid käsitlevad neid aspekte paremini ja puhtamalt.

Lõpetuseks tahaksin öelda, et funktsionaalsete abstraktsioonide täiendav täiustamine AOP-i veelgi paremaks asendamiseks võib olla kotliini keele edasise arengu paljutõotav vektor. Java-põhised AOP-raamistikud on tavaliselt JVM-spetsiifilised ja neid tajutakse kui läbipaistmatut võlu, samas kui Kotlini funktsionaalsed abstraktsioonid on tõesti platvormidevahelised ja kasutaja jaoks läbipaistvad.