如何扩展一个类却不使用继承

3.4 版本
维护中的版本

最重要文档 译注:这篇文档虽然很短,却极其重要,后面会有机会同大家深度分享。

要允许多个类向另一个类中添加方法,你可以在想要扩展的类中按照下例来定义 __call() 魔术方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Foo
{
    // ...
 
    public function __call($method, $arguments)
    {
        // create an event named 'foo.method_is_not_found'
        // 创建一个名为 'foo.method_is_not_found' 的事件
        $event = new HandleUndefinedMethodEvent($this, $method, $arguments);
        $this->dispatcher->dispatch('foo.method_is_not_found', $event);
 
        // no listener was able to process the event? The method does not exist
        // 没有监听能够处理此事件? 那么该方法不存在
        if (!$event->isProcessed()) {
            throw new \Exception(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
        }
 
        // return the listener returned value
        // 返回“监听器所返回的值”
        return $event->getReturnValue();
    }
}

这个类用到了一个特殊的 HandleUndefinedMethodEvent 事件类,也应当被创建。这是个通用类,每当你需要使用这种模式的“类扩展”时,都可以复用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
use Symfony\Component\EventDispatcher\Event;
 
class HandleUndefinedMethodEvent extends Event
{
    protected $subject;
    protected $method;
    protected $arguments;
    protected $returnValue;
    protected $isProcessed = false;
 
    public function __construct($subject, $method, $arguments)
    {
        $this->subject = $subject;
        $this->method = $method;
        $this->arguments = $arguments;
    }
 
    public function getSubject()
    {
        return $this->subject;
    }
 
    public function getMethod()
    {
        return $this->method;
    }
 
    public function getArguments()
    {
        return $this->arguments;
    }
 
    /**
     * Sets the value to return and stops other listeners from being notified
     * 设置返回值,同时中止向其他监听发送通知
     */
    public function setReturnValue($val)
    {
        $this->returnValue = $val;
        $this->isProcessed = true;
        $this->stopPropagation();
    }
 
    public function getReturnValue()
    {
        return $this->returnValue;
    }
 
    public function isProcessed()
    {
        return $this->isProcessed;
    }
}

接下来,创建一个监听类,用于监听 foo.method_is_not_found 事件,并添加 bar() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Bar
{
    public function onFooMethodIsNotFound(HandleUndefinedMethodEvent $event)
    {
        // only respond to the calls to the 'bar' method
        // 只对 'bar'方法的调用 进行响应
        if ('bar' != $event->getMethod()) {
            // allow another listener to take care of this unknown method
            // 允许另一个监听接管这个未知方法
            return;
        }
 
        // the subject object (the foo instance)
        // 被操作对象(主题对象。foo实例)
        $foo = $event->getSubject();
 
        // the bar method arguments
        // bar方法的参数
        $arguments = $event->getArguments();
 
        // ... do something / 做一些事
 
        // set the return value
        // 设置返回值
        $event->setReturnValue($someValue);
    }
}

最后,通过把 foo.method_is_not_found 事件连同 Bar 实例一起注册(到dispatcher),即可向 Foo 类中添加 bar 方法:

1
2
$bar = new Bar();
$dispatcher->addListener('foo.method_is_not_found', array($bar, 'onFooMethodIsNotFound'));

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)