使用行为(behavior)可以在不修改现有类的情况下,对类的功能进行扩充。通过将行为绑定到一个类,可以使得类具有行为本身所具有的属性和方法,就好像是类本来就具有的这些属性和功能一样。
好的代码设计,必须要同时满足可复用性、可维护性和可扩展性。设计原则中有一条非常重要的一条:类应该对扩展开放,对修改关闭。改变原有代码往往会带来潜在风险,因此我们尽量减少修改的行为。我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可以搭配新的行为。如果能实现这样的目标,有什么好处呢?这样的设计具有弹性,可以应对改变,可以接收新的功能来应对改变的需求。
Yii的行为就是这样一类对象,当一个对象(继承了Component的)想要扩展功能,又不想改变原有代码时,那么你完全可以用行为去实现这些新功能,然后绑定到该对象上——完全是符合“开闭原则”的。
Yii的行为都需要继承自yii\base\Behavior
,而能接受行为绑定从而扩充自身功能的只能是yii\base\Component的子类,只继承BaseObject基类没有继承Component的不能享受此“待遇”。因此,行为是组件才有的功能。行为和事件结合起来使用,还可以定义组件在何种事件进行何种反馈。因此行为有如下两个作用:
1. 将属性和方法注入到一个component里面,被访问时和别的属性或者方法访问无异(行为的附加)
2. 响应component中触发的事件,以便对某一事件作出反应(行为的绑定和触发,是对事件的应用)
定义行为
行为必须继承自yii\base\Behavior,定义一个行为仿照下面进行:
class MyBehavior extends \yii\base\Behavior
{
public $prop1;
private $_prop2;
private $_prop3;
//绑定事件和处理器,从而扩展类的功能表现,这里体现了“行为”字面意义
public function events()
{
}
//行为的只读属性
public function getProp2()
{
return $this->_prop2;
}
//行为的只写属性
public function setProp3($prop3)
{
$this->_prop3 = $prop3;
}
//行为的方法
public function foo()
{
return 'foo';
}
protected function bar()
{
return 'bar';
}
}
接下来,将行为附加到对象上,从而扩充对象的功能:
$user = new User();
//$user对像附加行为,扩充功能
$user->attachBehavior('myBehavior', new MyBehavior());
//获取prop2属性
$user->prop2;
//给只读属性赋值会报错
$user->prop2 = 3;
//给只写属性prop3赋值
$user->prop3 = 2;
//操作可读-可写属性prop1
$user->prop1 = 1;
$var = $user->prop1;
// 使用方法foo
$user->foo();
// 不可访问,这里会抛出'Unknown Method'异常
$user->bar();
当然MyBehavior()完全可以支持依赖注入,从而在运行时决定这些属性的值。
从上面可以看出,$user对象使用其MyBehavior的属性和方法来几乎毫不费劲,就像自己拥有这些属性和方法一样。但是,我们并没有给User类中添加任何一行代码,因此这个扩展做得真是悄无声息啊!
行为的附加
行为的附加或者绑定,通常是由Component来发起。有两种方式可以将一个Behavior绑定到一个 yii\base\Component 。 一种是静态附加行为,另一种是动态附加行为。静态附加在实践中用得比较多一些,因为一般情况下,在你的代码没跑起来之前,一个类应当具有何种行为是确定的。 动态附加主要是提供了更灵活的方式,上面即是行为的动态附加,但实际使用中并不多见。
静态附加
class User extends ActiveRecord
{
const MY_EVENT = 'my_event';
public function behaviors()
{
return [
// 匿名行为,只有行为类名
MyBehavior::className(),
// 命名行为,只有行为类名
'myBehavior2' => MyBehavior::className(),
// 匿名行为,配置数组
[
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop2' => 'value2',
],
// 命名行为,配置数组
'myBehavior4' => [
'class' => MyBehavior::className(),
'prop1' =>