AOP супраць функцый

Аспэктна-арыентаванае праграмаванне (AOP) даволі папулярна. Матывацыя да гэтага добра растлумачана ў адпаведным артыкуле Вікіпедыі.

AOP - выдатны інструмент для сапраўды глабальных паняццяў, такіх як пратакаляванне, якія не ўплываюць непасрэдна на логіку кода. Аднак праблемы з AOP выяўляюцца, калі ён выкарыстоўваецца для такіх рэчаў, як аўтарызацыя. Гэтыя аспекты прыкладання павінны быць добра бачныя ў адпаведным кодзе, каб распрацоўшчык мог адразу ўбачыць, ці правільна яны рэалізаваны пры чытанні адпаведнага зыходнага кода. Рамкі, заснаваныя на AOP, звычайна вырашаюць яго, выкарыстоўваючы анатацыі метадаў:

@RequireRole (Role.Admin) // добра бачны аспект
вясёлае абнаўленнеUserPermissions (…) {
    // Логіка тут
}

Аднак, з пункту гледжання чытальнасці, ён мала чым адрозніваецца ад функцыянальнага падыходу да той жа праблемы, выкарыстоўваючы функцыю RequRole замест анатацыі @RequireRole:

вясёлае абнаўленнеUserPermissions (…) {
    RequRole (Role.Admin) // кідае SecurityException
    // Логіка тут
}

Больш за тое, функцыянальны падыход мае перавагу ў тым, што ён таксама павялічвае да больш складаных праверак дазволаў, напрыклад, аналізуючы параметры метаду, перш чым вызначыць, якую ролю карыстальніка патрабуе.

Тое ж тычыцца і іншых аспектаў, такіх як транзакцыі. На жаль, функцыянальна ўяўляць больш складаныя паняцці на Java складана і нязручна, што стварае штучную папулярнасць для АОП у рамках экасістэмы Java.

Аднак гэта не з Катлінам. У Kotlin, замест Java-падыходу да транзакцый з AOP і такіх анатацыяў:

@Transactional
вясёлае абнаўленнеUserPermissions (…) {
    // Логіка тут
}

Гэта так жа чытэльна і чыста выглядае, калі перапісана ў функцыянальным парадку:

fun updateUserPermissions (…) = транзакцый {
    // Логіка тут
}

Перавага гэтага функцыянальнага падыходу заключаецца ў тым, што вы заўсёды можаце Ctrl / Cmd + націснуць на дэкларацыю транзакцыйнай функцыі ў IDE і адразу ж паглядзець, што менавіта яна робіць, што звычайна немагчыма ні ў адной з часта выкарыстоўваных рамак AOP. Нават калі навігацыя да зыходнага кода аспекту забяспечваецца плагінам IDE, расшыфроўка яго логікі патрабуе ведаў асобных багатых API і / або канвенцый.

На жаль, гэтая функцыянальная замена для AOP на аснове анатацыі ў Котліне не адразу прыводзіць да таго выпадку, калі да той жа функцыі ўжываюцца некалькі аспектаў, калі фігурныя дужкі і адступы пачынаюць збірацца:

fun updateUserPermissions (…) = прапісаны {
    транзакцыйны {
        // Логіка тут
    }
}

Арыентацыя складаецца ў тым, каб стварыць камбінаваную функцыю больш высокага парадку, каб забяспечыць захаванасць сайта з выкарыстаннем некалькіх аспектаў:

fun updateUserPermissions (…) = loggedTransactional {
    // Логіка тут
}

Яшчэ адным недахопам функцыянальнага падыходу з'яўляецца тое, што такія аспекты, як вядзенне рэгістрацыі, маюць патрэбу ў доступе да параметраў метаду. Звычайна яны даступныя непасрэдна ў традыцыйных рамках АОП праз спецыяльныя API, але функцыі Котліна не могуць лёгка атрымаць доступ да іх. Такім чынам, для таго каб рэальна прадставіць аспект рэгістрацыі ў рэальным жыцці чыста функцыянальным спосабам, трэба ўсё-ткі напісаць значную колькасць кодавых пласцін:

fun updateUserPermissions (params: Params) =
    logged ("updateUserPermissions ($ params)") {
        // Логіка тут
    }

Гэта разгляд па-ранейшаму робіць AOP інструментам выбару для рэгістрацыі, калі вам трэба мець яго ва ўсім дадатку глабальна і паслядоўна, але я лічу, што выкарыстанне AOP для такіх аспектаў, як аўтарызацыя і транзакцыі, з'яўляецца злоўжываннем, улічваючы багатыя функцыянальныя абстракцыі, якія даступныя ў Kotlin . Функцыі спраўляюцца з гэтымі аспектамі лепш і чысцей.

У заключэнне я б сказаў, што далейшае ўдасканаленне функцыянальных абстракцый для забеспячэння яшчэ лепшай замены AOP можа стаць перспектыўным вектарам будучай эвалюцыі для мовы Котліна. Якавыя рамкі AOP на аснове Java звычайна характэрныя для JVM і ўспрымаюцца як нейкая непразрыстая магія, у той час як функцыянальныя абстракцыі Kotlin сапраўды крос-платформавыя і празрыстыя для карыстальніка.