Yii框架的组件行为管理机制和Mix-in
本文包括以下内容:
- Yii框架的组件行为管理机制介绍
- Ruby、PHP5.4和Mix-in
在Yii框架的官网,我们可以看到关于Behaviors & events的介绍: Behaviors are simply a way of adding methods to an object.
我们看官网上的使用示例:
class SomeClass extends CBehavior{ public function add($x, $y) { return $x + $y; } } class TestComponent extends CComponent { } $test_comp = new TestComponent(); $test_comp->attachbehavior('blah', new SomeClass); $test_comp->add(2, 5);
在TestComponent类的对象创建的后,我们可以通过调用attachbehavior给对象添加新的方法。
通过其源码(在base/CComponent.php)可以知道它是通过在组件类内部以私有变量的方式存储这些添加的方法所在的对象, 通过魔术方法__call,当调用一个未定义的方法时需要调用__call方法的特性,遍历所有通过attachbehavior方法添加进来的对象, 并判断此对象是否禁用并且此对象是否存在需要调用的方法,如果存在则调用。
此种实现方式存在如下一些问题:
- 如果多个对象存在相同的方法,则程序调用时永远会调用第一次添加进去的方法
- 如果我们只是需要某个对象中的某个方法,但是在存储上需要将整个对象添加到列表中
也许你会觉得这些都是一些如果,都是一些假设,可能不会出现,这有些像众所周知的goto语句问题,如果用得好,这是一个利器,如果用得不好,可能会给你带来痛苦。 Yii框架中的这种机制实现运行时的方法绑定,虽然类的属性和实例参数仍然归属于其它类和对象。
在官方说明中也提到了这是一种类似于ruby语言的实现方式,如果我们用Ruby实现上面的方法该如何写呢?如下:
module SomeClass def add(x, y) return x + y end end class TestComponet include SomeClass end test = TestComponet.new puts test.add(10, 20)
非常简单的实现了类的重用,我们知道在PHP中,接口是可以多继承的,但是接口只是形态上的多继承,是一种对于类实现的约束,是一种规格。 如果要实现这种类的重用,Ruby受Lisp的影响引入了Mix-in,在PHP5.4引入了trait关键字。
在Ruby中Mix-in的关键字是module,而在即将推出的PHP5.4,其对应的关键字是trait; 如果要复用这个定义的类,在Ruby中使用include,而在PHP5.4中使用use。如下PHP代码:
<?PHP trait SomeClass { public function add($x, $y) { return $x + $y; } } class TestComponent { use SomeClass; } $obj = new TestComponent(); echo $obj->add(10, 20);
对于Mix-in类,有两个约束:
- 不能单独生成实例
- 不能继承其它的普通类
如果实例这个类程序执行会显示:
Fatal error: Cannot instantiate trait SomeClass...
如果从其它普通类继承会显示:
Fatal error: A trait (SomeClass) cannot extend a class ...
如果要查找这两个约束的源码实现,可以直接在源码中搜索Cannot instantiate trait和cannot extend a class。 从搜索可以看出:
- 不能单独生成实例的检测是在new关键字的中间代码执行时执行的,在Zend/zend_vm_execture.h文件
- 不能继承的约束是在编译成中间代码的过程中实现的,在 Zend/zend_compile.c文件
在面向对象编程语言,Mix-in是一个提供了一些被用于继承或在子类中重用的功能的类,它类似于一种多继承, 但是实际上它是一种中小粒度的代码复用单元,而不直接用于实例化。 虽然这不是一种专业的方式进行功能复用,这在实现多继承的同时,在一定程序上避免了多继承的明显问题。 一如Yii的组件行为管理机制,也是另外一种取巧的Mix-in实现。