自动装饰类中的每个方法

时间:2015-01-09 09:18:07

标签: php design-patterns decorator

我想在每次使用方法之前和之后添加一些逻辑(在私有,保护或公共时无关紧要)。

例如:

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();
    }
    ...
}

使用注释并不重要。有办法吗?

3 个答案:

答案 0 :(得分:1)

正如您的问题标题已经建议的那样,您可以使用Decorator 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将不会改变,假设方法名称不会改变(这实际上是一件坏事)。

但是也要查看wiki页面(或任何其他资源)以完全理解Decorator Pattern的意图。这(上图)可能不是您问题的理想解决方案。

编辑根据要求更自动,先生。

由于无法更改方法的可见性,因此使用魔术__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');

答案 1 :(得分:0)

我想,这是典型的智能参考模式(代理和装饰模式的混合)。

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();

但它仅适用于公共方法。

将智能引用与来自@giorgio和@yceruto的DecoratedService :: call()方法混合可以覆盖所有方法,或者只实现两次__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

答案 2 :(得分:-1)

使用magic __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();只有在涉及的方法受到保护或私有时才会执行;如果在对象内部调用

,则不会触发