Я хотел бы добавить некоторую логику до и после каждого использования метода (не имеет значения, является ли он закрытым, защищенным или общедоступным) в классе.
Например:
class Service
{
function test1() {
Log:start(__METHOD__);
someLogicInThere(); ....
Log:end(__METHOD__);
}
function test2() {
Log:start(__METHOD__);
someLogicInThere(); ....
Log:end(__METHOD__);
}
...
}
Моя идея, наконец, получить что-то вроде этого:
/**
* @LogDecorate
*/
class Service
{
function test1() {
someLogicInThere();
}
function test2() {
someLogicInThere();
}
...
}
Использовать аннотации не важно. Есть ли способ сделать это?
Как уже говорит ваш заголовок вопроса, вы можете использовать Декоратор Pattern за это. Однако я не совсем уверен, нужен ли здесь полный шаблон декоратора. Если это действительно простой вариант использования, то этого может быть достаточно.
Что вы можете сделать, это расширить класс и «направить» все вызовы в расширенный класс. Затем добавьте логику до и после и вызовите родительский метод между ними. Что-то вроде этого:
class Service {
function method1() {
doSomeFunkyStuff();
}
function method2() {
doSomeOtherFunkyStuff();
}
}
class DecoratedService extends Service {
function method1() {
Log::start(__METHOD__);
parent::method1();
Log::end(__METHOD__);
}
function method2() {
Log::start(__METHOD__);
parent::method2();
Log::end(__METHOD__);
}
}
$service = new DecoratedService();
$service->method1();
$service->method2();
Теперь вы можете выбрать оригинал Service
или используйте DecoratedService
, Функциональность та же, и DecoratedService
не нужно будет менять, если Service
изменения, при условии, что имена методов не изменятся (что на самом деле было бы плохо).
Но также загляните на вики-страницу (или любой другой ресурс), чтобы полностью понять, в чем заключается цель шаблона декоратора. Это (выше) не может быть идеальным решением для вашей проблемы.
РЕДАКТИРОВАТЬ немного более автоматически, как просили, сэр.
Поскольку вы не можете изменить видимость методов, используя магию __call()
не работает (общедоступные или защищенные родительские методы также будут доступны от ребенка). Но что ты Можно сделать, это создать свой собственный метод вызова!
class DecoratedService extends Service {
function call($method) {
if(!method_exists(parent, $method)) {
return false; // OR:
throw Exception;
// OR handle this case some other way
}
Log::start(array(parent, $method));
call_user_func(array(parent, $method));
Log::end(array(parent, $method));
}
}
$service = new DecoratedService;
$service->call('method1');
Использование магического метода __call может упростить вам задачу:
class Service
{
public function test1() {
echo 'TEST 1', PHP_EOL;
}
protected function test2() {
echo 'TEST 2', PHP_EOL;
}
public function __call($method, $args) {
echo 'Some stuff before', PHP_EOL;
$returnValue = $this->$method($args);
echo 'Some stuff after', PHP_EOL;
return $returnValue;
}
}
$x = new Service();
$x->test2();
$x->test1();
Обратите внимание, что если метод доступен извне класса (например, test1
) затем __call()
не будет вызван; он выполняется только в том случае, если используемые методы защищены или закрыты; и не срабатывает, если они вызываются внутри объекта
Я предполагаю, что это типичный случай Smart Reference Pattern (некоторое сочетание шаблонов Proxy и Decorator).
class A {
function test1() {
echo 'TEST 1', PHP_EOL;
}
function test2() {
echo 'TEST 1', PHP_EOL;
}
}
class ProxyA {
protected $wrapped;
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($name, $args) {
echo 'Log:start';
$this->wrapped->$name($args);
echo 'Log:end';
}
}
$proxy = new ProxyA(new A());
$proxy->test1();
Но это работает только для публичных методов.
Смешивание Smart Reference с методом DecoratedService :: call () из ответа @giorgio и @yceruto может охватывать все методы или просто реализовать __call () дважды:
class A {
public function test1() {
echo 'TEST 1', PHP_EOL;
}
private function test2() {
echo 'TEST 2', PHP_EOL;
}
public function __call($name, $args) {
if (method_exists($this, $name)) {
$this->$name($args);
}
}
}
class ProxyA {
protected $wrapped;
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($name, $args) {
if (method_exists($this->wrapped, $name)) {
echo 'Log:start';
$this->wrapped->$name($args);
echo 'Log:end';
}
}
}
$proxy = new ProxyA(new A());
$proxy->test0(); // Nothing to do
$proxy->test1(); // Done
$proxy->test2(); // Done