ЕВОЛУЦИЈА СОФТВЕРА ПОДРЖАНА АСПЕКТНО-ОРИЈЕНТИСАНИМ ПРОГРАМИРАЊЕМ Ментор: Драган Бојић Аутор: Саша Мисаиловић Пролеће 2008
Садржај Мотивација Раздвајање одлика Принципи АОП АОП Алати - AspectJ Аспектуална рефакторисања Коментари
Пример class Student { Map<Subject, Grade> grades; … Grade getGrade(Subject subject) { Grade result = grades.get(subject); return result; }
Пример – бележење class Student { Map<Subject, Grade> grades; … Grade getGrade(Subject subject) { Grade result = grades.get(subject); Logger.log(“Accessed subjects list for student “ + studentName); return result; }
Пример – аутентикација class Student { Map<Subject, Grade> grades; … Grade getGrade(Subject subject) { AuthService.checkPermissions(session); Grade result = grades.get(subject); Logger.log(“Accessed subjects list for student “ + studentName); return result; }
Пример – обрада изузетака class Student { Map<Subject, Grade> grades; … Grade getGrade(Subject subject) { try { AuthService.checkPermissions(session); Grade result = grades.get(subject); Logger.log(“Accessed grade for “ + subject + “ for student “ + studentName); return result; } catch (AuthenticationException e) { // handle exception } finally { // handle common case - authentication }
Пример - трансакције class Student { Grade getGrade(Subject subject) { try { AuthService.checkPermissions(session); TransactionManager.startTransaction(this); Grade result = getSubjectsTable().getEntry(subject); TransactionManager.endTransaction(this); Logger.log(“Accessed subjects list for student “ + studentName); return result; } catch (AuthenticationException e) { // handle exception } finally { // handle common case - authentication }
Пример – додатна обрада изузетака class Student { Grade getGrade(Subject subject) { try { AuthService.checkPermissions(session); TransactionManager.startTransaction(this); Grade result = getSubjectsTable().getEntry(subject); TransactionManager.endTransaction(this); Logger.log(“Accessed grade for “ + subject + “ for student “ + studentName); return result; } catch (AuthenticationException e) { // handle exception } catch TransactionException e) } finally { // handle common case – authenticatoin // handle common case - transaction } } }
Пример – коначан изглед class Student { Grade getGrade(Subject subject) { try { AuthService.checkPermissions(session); TransactionManager.startTransaction(this); Grade result = getSubjectsTable().getEntry(subject); TransactionManager.endTransaction(this); Logger.log (“Accessed grade for “ + subject + “ for student “ + studentName); return result; } catch (AuthenticationException e) { // handle exception } catch TransactionException e) } finally { // handle common case – authenticatoin // handle common case - transaction } } } class Student { Grade getGrade(Subject subject) { Grade result = grades.get(subject); return result; }
Пример – коначан изглед class Student { Grade getGrade(Subject subject) { try { AuthService.checkPermissions(session); TransactionManager.startTransaction(this); Grade result = getSubjectsTable().getEntry(subject); TransactionManager.endTransaction(this); Logger.log (“Accessed grade for “ + subject + “ for student “ + studentName); return result; } catch (AuthenticationException e) { // handle exception } catch TransactionException e) } finally { // handle common case – authenticatoin // handle common case – transaction } } }
Садржај Мотивација Раздвајање одлика Принципи АОП АОП Алати - AspectJ Аспектуална рефакторисања Коментари
Раздвајање одлика Separation of concerns Идентификујемо одлике које су међусобно независне и обрађујемо их као засебне компоненте Један од основних принципа развоја софтвера Једноставније разумевање проблема Већа могућност одржавања и проширивања Поновна употребљивост делова програма Побољшана поузданост Подршка методологија програмирања: Процедуре и функције, библиотеке Објекти и класе, пакети
Раздвајање одлика Едсгар Дајкстра (1974. “О улози научне мисли”): […]It is what I sometimes have called "the separation of concerns", which, even if not perfectly possible, is yet the only available technique for effective ordering of one's thoughts, that I know of. This is what I mean by "focusing one's attention upon some aspect": it does not mean ignoring the other aspects, it is just doing justice to the fact that from this aspect's point of view, the other is irrelevant.
Прожимајуће одлике Crosscutting concerns Неке одлике се не могу локализовати у једном модулу, већ се њихова имплементација преплиће кроз хијерархију декомпозиције система Неке одлике (а) не представљају основну карактеристику модула и/или (б) нису предвиђене у време пројектовања Циљ: Сваки модул треба да обавља искључиво своју основну функционалност
Прожимајуће одлике Логовање y org.apache.tomcat
Прожимајуће одлике Пресликавање из захтева у имплементацију није бијективно Проблеми у имплементацији: Расејавање кода(scattering) Мршење кода (tangling) Декомпозициона тиранија Предности има декомпозиција по основним (пословним) захтевима. Остали (административни) захтеви се декомпонују у зависности од примарне декомпозиције Одражава се на цео животни циклус софтвера
Прожимајуће одлике Примери Проблеми : Контрола приступа Трансакције Расејавање Мршење Примери Контрола приступа Трансакције Праћење извршавања (tracing & logging) Управљање сесијом Кеширање Обрада изузетака Policy enforcement Тестирање … Component
Прожимајуће одлике Издвајање кода секундарних одлика у засебне модуле: Издвајање кода секундарних одлика у засебне модуле: Флексибилност Прегледност Независност Сваки модул представља независну одлику Модул се зове аспект component aspects
Садржај Мотивација Раздвајање одлика Принципи АОП АОП Алати - AspectJ Аспектуална рефакторисања Коментари
Аспектно-оријентисано програмирање Раздваја примарне одлике од секундарних Постоји посебан модул, аспект, који представља секундарне одлике и начин повезивања са модулима примарних одлика Омогућава додавање особина (делова стања и понашања) модулу који имплементира примарну одлику Представља надградњу објектно-оријентисаног програмирања Сви концепти из ОО су неизмењени!
Сродан приступ Пројектни образац Декоратер Сродан приступ Пројектни образац Декоратер BufferedReader in = new BufferedReader(new FileReader("foo.in"));
Сродан приступ Пројектни образац декоратер Сродан приступ Пројектни образац декоратер Мора се узети у обзир приликом иницијалног дизајна система или се оригинални систем мора рефакторисати Мора се експлицитно инстанцирати на свим местима у коду у којима је потребан Компликовано искључивање/промена додатне могућности Да ли се сме користити за додавање сигурности? Флексибилно решење Решава проблем додавања новог стања и новог понашања за објекте
Сродан приступ Пројектни образац Пресретач Пресретач (Interceptor) Образац за конкурентне и мрежне програме Омогућава додавање одговорности основним објектима током извршавања програма Циљни објекат на одређеним местима оглашава докле је извршавање стигло тачке пресретања Пријављени пресретачи могу да у том тренутку изврше промену стања циљног објекта class TargetClass { TargetClassState state; List<Interceptor> executeEntry; List<Interceptor> executeExit; void execute() { notify(executeEntry); // method body, change state notify(executeExit); } void notify(List<Interceptor> event) { for( Interceptor i : event ) i.report(state);
Сродан приступ Пројектни образац Пресретач class TargetClass { TargetClassState state; List<Interceptor> executeEntry; List<Interceptor> executeExit; void execute() { notify(executeEntry); // method body, change state notify(executeExit); } void notify(List<Interceptor> event) { for( Interceptor i : event ) i.report(state); Потенцијални недостаци: Тачке пресретања мора да дефинише експлицитно програмер циљне класе Програмер клијентског кода мора ручно додати пресретаче, или да користи апстрактну фабрику Губи се на перформансама уколико на некој тачки пресретања не постоји ниједан пресретач
Аспектно-оријентисано програмирање Join-point модел за повезивање одлика Кључни појмови: Join point – локација у програму која се може идентификовати (нпр. улаз/излаз из методе) Pointcut – скуп више Join point-a који имају заједничку особину (нпр. припадност класи, заједничко име) Advice – програмски кôд који имплементира секундарне одлика Introduction – додатни поља и методи који су потребни за имплементирање секундарних одлика Aspect – реализује секундарне одлике, идентификацијом места у програму (помоћу Pointcut-a) где треба извршити акције (наведене у Advice-у) и додавање потребних метода и поља (наведене Introduction-ом) потребних за извршење акције
Особине АОП Сав специфичан код аспекта се наводи на једном месту Секундарне одлике Лако се могу прикључити/развезати од примарног кода Прикључују се само уколико су потребне Нису потребне ручне промене кода Може се додати у било којој фази рада на систему (у било којој верзији) Скуп места на којима треба извршити специфични код аспекта се наводи имплицитно, унутар аспетка Оригинални код нема знања о аспектима и о њиховим специфичностима (нити зависи од њих!) Могућност поновног коришћења
Критика АОП Мења семантику језика? Разбија локализацију секвенце кода Еволуција оригиналног кода може довести до проблема при коришћењу аспекта Промена понашања туђег (3rd party) кода Имплементације концепата се доста разликују међу алатима и језицима Џејмс Гослинг о АОП-у: “I basically like the concept, but I don’t think that the research community has really figured out how to encapsulate those in a mechanism that really works for people. Its like giving them a chainsaw without any safety instructions.”
Садржај Мотивација Раздвајање одлика Принципи АОП АОП Алати - AspectJ Аспектуална рефакторисања Коментари
АОП Алати Постоји велики број алата за различите језике који на различите начине имплементирају АО парадигму Јава AspectJ Проширење програмског језика Јава Интеграција са Eclipse радним окружењем Постоји још алата за Јаву: HyperJ, GluonJ… Постоје алати за С#: Aspect#, PostSharp… AspectC , за процедурални језик C, AspectF# за функционални језик F# Окружења попут Spring и JBoss користе концепте АОП
АОП Алати АО може да се примени на Нивоу класе (type level) Нивоу објекта (instance level) Плетење (weaving)- Комбиновање АО кода са ОО кодом може да се примени Статички – у време превођења Над изворним кодом Над међукодом/извршним кодом Динамички – у време извршавања
AspectJ http://www.eclipse.org/aspectj/ Проширење програмског језика Јава Нове кључне речи (aspect, pointcut…) Компатибилан byte code, извршава се на свакој JVM Могућност статичког и динамичког плетења У време превођења - посебан преводилац (ajc), подржава Јаву 5 (и 6) У време учитавања класе, помоћу анотација или XML докумената Подршка у Eclipse радном окружењу http://www.eclipse.org/ajdt/ Едитор: истицање синтаксе, обележавање join points, допуна кода, ... Преводилац – превођење и покретање програма Дебагер – интегрисан (обележавање кода који се извршава...)
AspectJ – проширење језика Јава Нови конструкт језика, налик класи, садржи остале елементе Pointcut Идентификују жељене Join Point-е (нпр. део имена) Врсте Join points: Позив метода или конструктора Извршавање метода или конструктора Приступ или промена вредности поља Иницијализација класа и објеката Извршавање руковаоца изузетака Синтакса попут регуларних израза; врши се pattern matching Advice – наводе се у аспекту, уз одговарајући pointcut Место извршавања: before, after, around Код који представља тело advice-a Introduction – поља и методи у аспекту
AspectJ - пример Уколико желимо да постигнемо: class C { void m1(int i) { Logger.log(“C.m1(int) started”); // execute operation body m1… Logger.log(“C.m1(int) finished”); } int m2() { Logger.log(“C.m2() started”); // execute operation body m2… Logger.log(“C.m2() finished”); return … // …
AspectJ - пример class C { void m1(int i) { /* execute operation body m1… */ } int m2() {/* execute operation body m2… */; return … } } aspect Logging { pointcut c_m() : execution(void C.m1(int)) || execution(int C.m2()); // modifier_pattern ret_type_pattern type_pattern.id_pattern(..)) pointcut c_mx() : execution (* C+.m*(..)); before() : c_m() { Logger.log(thisJoinPoint.getSignature() + " started"); } after() : c_mx() { Logger.log(thisJoinPoint.getSignature() + " finished"); } }
AspectJ - имплементација Две фазе у превођењу код ajc: Front-end: генерише Јава .class фајлове, регуларне за обичне класе, а са анотацијама за аспекте да представи додатне елементе као што су pointcut и advice Back-end: примењује трансформације које упарују Join points са advice-ом, на основу задатог pointcut-a; инструментизује класe у којима постоји join point тако што умеће позив advice методе. *C.class, Logger.class, AspectLogging.class C.java, Logger.java C.class, Logger.class, @AspectLogging.class FЕ BE Logging.aj
AspectJ - имплементација Елементи АОП се преводе у елементе ООП, који се могу покренути у извршном окружењу: Аспект се преводи у класу Pointcut се представља анотацијом која садржи правило за упаривање са Join Point-има; анотација припада аспекту. Advice се представља као метод класе која настаје од аспекта; место примене advice-а се обележава посебном анотацијом Статичка инструментација на нивоу класе Инструментизује се byte code - BCEL библиотека Може се извршавати на било којој Јава VM
AspectJ - пример class C { void m1(int i) { /* execute operation body m1… */ } int m2() {/* execute operation body m2… */; return … } } aspect Logging { pointcut c_m() : execution( void C.m1(int)) || execution(int C.m2()); pointcut c_mx() : execution (* C+.m*(..)); before() : c_m() { Logger.log(thisJoinPoint.getSignature() + " started"); } after() : c_mx() { Logger.log(thisJoinPoint.getSignature() + " finished"); } }
AspectJ – имплементација пример @aspect(“Logging”) @pointcut(“c_m”, “execution( void C.m1(int)) || execution(int C.m2())”) public class AspectLogging { @advice(before, “c_m”) public static void before_adv_1(java.lang.reflect.Method m) { Logger.log(m.getSignature() + “ started”); } //… } class C { void m1() { AspectLogging.before_adv_1(C.class.getMethod(“m1”, new Class []{int.class})); // code of m1… AspectLogging.after_adv_1(C.class.getMethod(“m1”, new Class []{int.class})); }
Садржај Мотивација Раздвајање одлика Принципи АОП АОП Алати - AspectJ Аспектуална рефакторисања Коментари
АОП у еволуцији софтвера Додавање нових одговорности постојећим класама Креирање нових аспеката и повезивање са класама Смањене међузависности између модула Рефакторисање постојећих секундарних одговорности Смањује се распршеност и умршеност кода Систематска подршка за делимично имплементиране одговорности После рефакторисања одговорности се ефикасно могу проширити на класе у којима нису постојале одговорности се ефикасно могу изменити Међутим, нека од постојећих ОО рефакторисања могу бити опасна и морају се прилагодити (нпр. промена имена методе!)
Екстраховање позива метода [Fowler] Код који се понавља се смешта у засебан метод, а на ранијим местима кода се наводи позив тог метода Није АО, важи на основу једне класе, позиви зависе од постојања метода у датој класи [АО рефакторисање] Код који се понавља се тражи на нивоу целог пројекта: Тело екстрахованог метода се смешта у аспект (introduce) Сва места на којима је пронађен позив методе се идентификују помоћу pointcut-a За тај pointcut се додељује advice који позива екстраховану методу, која је сад у аспекту, а позив у оригиналној класи се брише Оригиналне класе постају независне од функционалности коју је обезбеђивао екстраховани метод
Екстраховање позива метода
Екстраховање имплементације интерфејса Замењује апстрактну класу аспектом Ефективно, омогућава вишеструко наслеђивање Све дуплиране (идентичне) имплементације се такође рефакторишу аспектом Али, све класе на које се односи нови аспект постају зависне од њега (не могу се користити као ОО класе)
Екстраховање руковања изузецима Проблем: На више места у клијентском коду се позива метода која може бацити неке изузетке; на свим местима врши се идентична обрада! Предлог: Обрада изузетка за тај позив методе се премешта у аспект. Pointcut тог аспекта је сваки позив методе Advice тог аспекта се извршава када метод баци неки изузетак Око позива методе остаје само подразумевани try {…} catch (Throwable t) { throw new RuntimeException(t); } блок Особине: Обрада изузетка је локализована; промена у аспекту се одражава на све означене позиве Код на месту позива је једноставнији за праћење Треба обратити пажњу на случај када за неке позиве метода треба урадити другачију обраду изузетака
Замена редефинисања метода advice-ом Додавање нових /промењених одговорности постојећим класама: Класа се наследи; у наслеђеној класи се редефинишу методе Пројектни обрасци: декоратер и сл... Аспект се такође може користити за додавање нове одговорности: Методи којима треба додати одговорност се наведу у pointcut-у, a у advice-у се наводи нови код (нпр. позив посматрача – observer-a) Ефекат сличан као код пројектног обрасца Пресретач
Policy Enforcement Ефикасна котрола исправности кода Евиденција Ефективно, проширује се функција компајлера Евиденција У време превођења У време извршавања Пример: aspect DetectConsoleUsage { declare warning : get ( * System.out) || get ( * System.err) : “Consider using Logging.log() instead of System.out and System.err”; }
Садржај Мотивација Раздвајање одлика Принципи АОП АОП Алати - AspectJ Аспектуална рефакторисања Коментари
Закључак Аспектно оријентисано програмирање је техника која помаже да се независне одлике сместе у засебне модуле Смањује расејавање и мршење кода Једноставније пресликавање из спецификације у имплементацију АОП уводи неколико принципа који се надограђују на принципе постојећих програмских парадигми Може се применити на ОО, процедуралне или функционалне програме Постоји велики број алата за различите програмске језике АОП омогућава већу флексибилност, прегледност и независност компоненти у систему
Литература Ramnivas Laddad, AspectJ in Action: Practical Aspect-Oriented Programming, Manning, 2003. Tzilla Elrad, Robert E. Filman and Atef Bader, Aspect-Oriented Programming, Communications of the ACM, Vol. 44, No. 10, October 2001, pp. 29-32. Adrian Colyer, Andy Clement, George Harley, Matthew Webster, Eclipse AspectJ: Aspect-Oriented Programming with AspectJ and the Eclipse AspectJ Development Tools, : Addison Wesley Professional, 2004