PHP和多重继承;我知道你可以;t、 但是我该怎么做呢.


PHP and multiple inheritance; I know you can't, but then how do I..?

我知道PHP中根本不支持多重继承1,尽管存在许多"破解"或变通方法来模拟它,但我也知道像对象组合这样的方法可能比这种变通方法更灵活、更稳定、更容易理解。奇怪的是,PHP 5.4的特性将是合适的解决方案,但我们还没有完全做到,是吗。

现在,这不仅仅是一个">脒亚硝酸盐?"的问题,但我想确保我的方法对其他人有意义。

假设我有类ActionEvent(还有更多,但我们将保持简短(,并且它们都需要(near的(相同的方法,显而易见的方法是;创建一个公共基类,扩展并执行;毕竟,它们在概念上是相似的,足以构成类层次结构中的兄弟姐妹(我认为(

问题是Event需要扩展一个类(Exception(,而该类本身无法扩展任何内容。方法(和属性(都与"属性"值有关,我们将它们称为"选项"answers"数据",其中"选项"是存储在类级别的值,"数据"是存储于实例级别的值。

除了(没有双关语(Exception类之外,我可以简单地创建一个所有相关对象都可以扩展的公共类,以继承必要的功能,但我想知道我能做些什么来避免Event中看似不可避免的代码重复;另外,其他概念上不太相似的类也需要此功能。

到目前为止,答案似乎是,使用对象组合方法,创建一个Data类,并在两个方面管理它:

  • 在对象实例化时,创建一个Data实例,作为"数据"与对象一起使用
  • 在某个时刻(通过静态initialize()方法,可能(创建一个Data实例,作为"选项"与类一起静态使用

例如,名为IDataIOption的接口将由需要此功能的类来实现。IData只是在使用者上强制执行Data类的实例方法,并且调用将转发到实例Data属性对象,而IOption将强制执行类似命名的方法(用"data"代替"option">(,并且这些方法将转发到静态Data属性对象。

我看到的是这样的东西(这些方法在外观上有点天真,但为了简洁起见,我在这里对它们进行了精简(:

interface IData{
    public function setData($name, $value);
    public function putData($name, &$variable);
    public function getData($name = null);
}
interface IOption{
    public static function initializeOptions();
    public static function setOption($name, $value);
    public static function setOptions(Array $options);
    public static function getOptions($name = null);
}
class Data implements IData{
    private $_values = array();
    public function setData($name, $value){
        $this->_values[$name] = $value;
    }
    public function putData($name, &$variable){
        $this->_values[$name] = &$variable;
    }
    public function getData($name = null){
        if(null === $name){
            return $this->_values;
        }
        if(isset($this->_values[$name])){
            return $this->_values[$name];
        }
        return null;
    }
}
class Test implements IData, IOption{
    private static $_option;
    private $_data;
    public static function initializeOptions(){
        self::$_option = new Data();
    }
    public static function setOption($name, $value){
        self::$_option->setData($name, $value);
    }
    public static function setOptions(Array $options){
        foreach($options as $name => $value){
            self::$_option->setData($name, $value);
        }
    }
    public static function getOptions($name = null){
        return self::$_option->getOptions($name);
    }
    public function __construct(){
        $this->_data = new Data();
    }
    public function setData($name, $value){
        $this->_data->setData($name, $value);
        return $this;
    }
    public function putData($name, &$variable){
        $this->_data->putData($name, $variable);
        return $this;
    }
    public function getData($name = null){
        return $this->_data->getData($name);
    }
}

那么我该何去何从?我无法摆脱这样一种感觉,即我正在远离好的设计;我在客户端类和存储类之间引入了一种不可逆的依赖关系,接口无法显式地强制执行这种依赖关系。


编辑:或者,我可以将对Data(,必要时(的引用保持为公共,从而消除了对代理方法的需要,从而简化了组合。那么,问题是,我不能偏离Data类的功能,比如说,如果我需要使getData()递归地操作,正如这个片段所示:

function getData($name = null){
    if(null === $name){
        // $parent_object would refer to $this->_parent
        // in the Test class, given it had a hierarchal
        // implementation
        return array_replace($parent_object->getData(), $this->_values);
    }
    // ...
}

当然,这一切都可以归结为基于每个类的单独定义,以支持与默认实现的任何偏差。

我想这里的结局是,我很难理解哪里的代码重复是"可以的"(或更准确地说,不可避免的(,哪里可以将通用功能提取到容器中,以及如何跨类引用和使用所包含的功能,在必要时偏离(通常可忽略。同样,traits(在我对beta的粗略测试中(似乎非常适合这里,但组合原理早在5.4之前就已经存在了(和PHP完全是这样(,我确信有一种"经典"的方法可以实现这一点。


1.有趣的是,维基百科的多重继承页面已经被标记为版权调查。钻石问题似乎是一个合适的替代品。

编辑:我刚刚又读了你的问题,你似乎在暗示你实际上在使用getter和setter来处理数据。如果是这样的话,你能为我提供更多关于你试图实现的目标的细节吗。我怀疑,您决定如何对对象和数据进行建模,是导致您出现这种情况的原因,并且一种替代方案可以解决问题。

您不需要多重继承。您甚至不需要您编写的大部分代码。

如果类"Data"answers"Option"的目的是简单地存储数据,则使用数组。或者,如果您喜欢对象的语法,请将数组转换为stdClass的对象或实例:

$person = (object)array(
    'name' => 'Peter',
    'gender' => 'Male'
);
OR
$person = new stdClass;
$person->name = 'Peter';
$person->gender = 'Male';

拥有一大堆实际上对数据没有任何作用的getter和setter是毫无意义的。