一、何谓Mixin?
Mixin是一种编程的风格,在Lisp等语言里面早已有了,但我首次知道这个概念是在Ruby语言中。我对Mixin的理解是:一种行为注入(Behavior
Injection)的方法。要了解Mixin,也就是“行为注入”的好处,需首先了解OOP中的“多重继承”和“接口”的概念。
在我的印象中,接口是在继承这个概念出现以后才进入主流语言的。首先有了C++的多重继承,然后才有了Java中否定多重继承而代之以接口的概念(当然,一些先驱性或革命性的语言如Lisp、Eiffel可能早就有此类概念了)。接口带来的好处不言而喻,但它仍然有不够灵活的地方:如果一个类声明了某个接口,它就必须实现此接口,而如果它没有声明某接口,也就无法在运行时动态地获取此接口相应的行为。
Mixin则灵活的多。它相对于接口的优势在于,无需规定接口,可以随时添加/删除行为。这种动态添加行为的能力非常有用。它可以让我们写出既面向对象而又面向切面 的代码。比如,在ERP系统中有一个员工类。假设“主管”级别的员工有查看报表的权利,我们可以利用Mixin,使得系统中只需要一个员工类,就可以方便 的实现权限控制 -- 只需要在合适的地方注入“主管”行为即可,而无需针对员工类派生出各种具有不同权限的主管类、工人类等等,或者到处写一大堆复杂的、不那么OO的权限判断代码。
二、在PHP中实现Mixin
先来看一下代码:
<?php
class Greeting {
public function greet($obj) {
echo $obj->getName()." says hello\n";
}
private function __construct() {} //一个private的构造函数使得这个类不能直接被实例化
}
class Mixable {
private $_mixinlookup = array();
public function __construct() {
if (is_array($this->mixins)) {
foreach($this->mixins as $mixin) {
$methods = get_class_methods($mixin);
if (is_array($methods)) {
foreach($methods as $method) {
$this->_mixinlookup[$method] = $mixin;
}
}
}
}
}
public function __call($method, $args) {
if (isset($this->_mixinlookup[$method])) {
$class = $this->_mixinlookup[$method];
array_unshift($args, $this);
return call_user_func_array(array($class, $method), $args);
}
trigger_error('Call to undefined function '.$method, E_USER_WARNING);
}
}
class Mixture extends Mixable {
protected $mixins = array("Greeting");
public function getName() {
return get_class();
}
}
$o = new Mixture();
$o->greet();
?>
class Greeting {
public function greet($obj) {
echo $obj->getName()." says hello\n";
}
private function __construct() {} //一个private的构造函数使得这个类不能直接被实例化
}
class Mixable {
private $_mixinlookup = array();
public function __construct() {
if (is_array($this->mixins)) {
foreach($this->mixins as $mixin) {
$methods = get_class_methods($mixin);
if (is_array($methods)) {
foreach($methods as $method) {
$this->_mixinlookup[$method] = $mixin;
}
}
}
}
}
public function __call($method, $args) {
if (isset($this->_mixinlookup[$method])) {
$class = $this->_mixinlookup[$method];
array_unshift($args, $this);
return call_user_func_array(array($class, $method), $args);
}
trigger_error('Call to undefined function '.$method, E_USER_WARNING);
}
}
class Mixture extends Mixable {
protected $mixins = array("Greeting");
public function getName() {
return get_class();
}
}
$o = new Mixture();
$o->greet();
?>
在这个例子中,Mixture类被注入了Greeting这种行为而具备了greet()的能力。这种行为是在运行中可以随时改变的。想象一下这种能力的用途吧。
三、针对原文的改进之处
原文中使用了eval方法来实现Mixin,带来了两个弊端:1)被资深PHP人一致痛斥的eval具有很大的安全隐患和很差的性能;2)我发现按照原方案,这些被注入的方法只能接受简单的数字或字符串参数,而不能使用复杂的如array或object类型的参数。
改进的方法是,避免在需要注入的方法中使用$this,而是学习python,在方法的第一个参数传入当前对象的指针(也就是例子代码中greet的参数$obj)。只要习惯了这种做法,其实它和$this是一样方便的。
没有评论:
发表评论