я про этот пример. какой тут механизм? Код (PHP): // test.php class Test { function testfun() { /* тут описана работа метода */ $test = "Hello"; /* тут описана работа метода */ return $test; } } // aspecttest.php class AspectTest { private $log = new Log(); function before($class,$method) { $this->log->save("Run $method!"); } function after($class,$method,$returnvalue) { $this->$log->save("End $method!"); $this->$log->save("$method return $returnvalue"); } } $aop = new AspectAOP(); /* регистрируем наш экземпляр который будет реагировать на вызов метода testfun класса Test; Так же можно объявить array('Test::*') где будет реагировать на любой вызываемый метод класса Test */ $aop->register(new AspectTest(), array('Test::testfun')); $test = new Test(); echo $test->testfun();
Ну про враппер я тебе зачем тут в нескольких постах разъяснял? Если в целом то: $aop = new AspectAOP(); - На этом этапе я создаю объект тип которого был мною предопределен заранее. Он, в свою очередь в конструкторе, определяет собственный враппер для перехвата подключаемых классов (аля require("test.php")). $aop->register(new AspectTest(), array('Test::testfun')) - Тут происходит регистрация методов before/after класса AspectTest к методу testfun класса Test. $test = new Test(); - в этот момент, как только автоматически подгружается файл класса (средствами spl_autoload_register), враппер класса AspectAOP, токенизит(token_get_all) подгруженный файл, сравнивает с зарегистрированными методами (array('Test::functest')) и вслучае совпадения, переписывает метод functest основываясь на реализацию подсунутую в классе AspectTest. На выхлопе враппера, подгруженный test.php имеет например такую картину: Код (PHP): class Test { function testfun() { $args = func_get_args(); return self::__aopCaller(__FUNCTION__, 'aop__testfun', $this, $args); } private static function __aopCaller($args) { /* тут мы пытаемся выяснить кто нас мог вызывать, то есть получаем доступ к экземпляру класса AspectTest и последовательно выполняем соответствующие методы before/aop__testfun/after */ } function aop__testfun() { /* тут описана работа метода */ $test = "Hello"; /* тут описана работа метода */ return $test; } } ну как-то так, для наглядности
по простому говоря, файл php является "шаблоном", а выполняется какой-то другой, созданный на основе этого плюс код, сгенеренный из аннотаций. минусы очевидны, а плюсы как-то нет
Если вы пишите файл php используя в каждом методе заведомо нужный функционал (скажем для логирования), и клонируете его в другие подобные файлы - тоже самое что и генерация кода на основе "шаблона" о котором вы говорите. Плюс второго это наименьшая связанность, мне достаточно переопределить всего лишь методы, а то и просто менять целые классы в качестве AOP для решения задач в "шаблоне", а в случае перового Вам придется переписывать все ваши упоминания во всех ваших файлах =) И да, я могу управлять: нужно ли мне менять шаблон или нет, что в случае принудительно написанного функционала приводит к уменьшению покрытия кода из-за отсутствия надобности использования того самого функционала.
Скорее на фабрику. Всё же почитать стоит =) Суть не в том, что везде где мне нужно (скажем логировать) надо воткнуть фабрику, декоратор, сервис локатор, да вообще что угодно. Суть в том, что я избавляю себя от написания кода, который может скажем не понадобиться на продакшене, ну допустим: Код (PHP): // Loger должен логировать все в development function connectToDb($args) { Loger::factory('File_Loger')->save("Connect to database", $args); .............. Loger::factory('File_Loger')->save("Result connect to database", $result); } // Но в production должно быть так (без всяких логирований): function connectToDb($args) { .............. } Ну а если время от времени я должен использовать фабрику не File_loger, а скажем Dd_Loger, или скажем Mem_loger? Получается, я каждый раз должен переписывать все функции где используется другая фабрика. Вы конечно скажете, что можно сделать фабрику фабрик -) Однако это тоже не подходит, так-как например я обязан часть функций логировать скажем в File_Loger, а другую часть в Mem_loger, а то и вообще менять местами все это дело. Моя же цель, избавить себя от программирования всех этих фабрик, декораторов и прочих петтернов, для того, что бы логгировать, пулять транзакции и много чего еще. То есть не писать данный функционал в рабочих скриптах, а реализовать его отдельно, и в случае надобности, скажем на продакшене, оперативно получить информацию от том, что происходит в тот или иной момент в какой-нить функции(ях). То есть код у меня минимальный (только функционал который заложен в функцию, метод и т.п.), и я его могу расширить на лету не меня исходного кода. Включил - глянул, выключил..
Ну не надо преувеличивать сложности. Чтобы заменить класс логгера в общем случае не требуется переписывать место где он используется. IoC существует не только в AOP На продакшене можно логгер заменить на заглушку, которая ничего не делает. Меня как-то напрягает, что с AOP мы не только не видим внутренности "черного ящика", но даже не знаем есть ли он вообще и как происходит обращение к нему.
IoC был для примера и не является для АОП панацей. Меня напрягают заглушки так-как они не только увеличивают время выполнения алгоритмов, так еще и время написания кода. Рабочему коду совершенно не нужно знать о существовании АОП и его реализации, он как работал, так и работает. И да, АОП это не каждодневный кодинг, его необходимость используется в частных случаях. Для меня АПО успешно справляется с пересечениями (crosscutting) что представляет для меня большую пользу.
AndreJM, вообще штука интересная. Раз уж ты засветился с аспектами, можно тебя поспрашивать? где еще кроме логирования выгодны аспекты? ты писал, что использовал stream_wrapper для фокуса с аспектами — не могу понять как он может участвовать. автолоадер классов это понятно, а свой протокол зачем???
Часто это употреблялось мной еще и в: транзакциях, эксепшенах, ... Иногда даже для реализации некоторых шаблонов, например адаптер, да и вообще наверное для создания комплексной логики в целом. Протокол нужен был для обхода стандартных врапперов, например тот же 'file://'. Ну то есть привычнее работать с собственной обёрткой =) Моя задача подключать (через автолоадер) стандартные(привычные) файлы с расширением.php и в тоже время я должен контролировать их загрузку и в случае определенных условий... короче.. обычная импликация. Навешиваем обертку на загружаемый класс если загружаемый класс подходит под мои условия зарегистрированного ранее аспект-объекта. Ну и на выхлопе уже переработанный класс. Надеюсь доходчиво объяснил =)