我知道PHP中根本不支持多重继承1,尽管存在许多"破解"或变通方法来模拟它,但我也知道像对象组合这样的方法可能比这种变通方法更灵活、更稳定、更容易理解。奇怪的是,PHP 5.4的特性将是合适的解决方案,但我们还没有完全做到,是吗。
现在,这不仅仅是一个">脒亚硝酸盐?"的问题,但我想确保我的方法对其他人有意义。
假设我有类Action
和Event
(还有更多,但我们将保持简短(,并且它们都需要(near的(相同的方法,显而易见的方法是;创建一个公共基类,扩展并执行;毕竟,它们在概念上是相似的,足以构成类层次结构中的兄弟姐妹(我认为(
问题是Event
需要扩展一个类(Exception
(,而该类本身无法扩展任何内容。方法(和属性(都与"属性"值有关,我们将它们称为"选项"answers"数据",其中"选项"是存储在类级别的值,"数据"是存储于实例级别的值。
除了(没有双关语(Exception
类之外,我可以简单地创建一个所有相关对象都可以扩展的公共类,以继承必要的功能,但我想知道我能做些什么来避免Event
中看似不可避免的代码重复;另外,其他概念上不太相似的类也需要此功能。
到目前为止,答案似乎是,使用对象组合方法,创建一个Data
类,并在两个方面管理它:
- 在对象实例化时,创建一个
Data
实例,作为"数据"与对象一起使用 - 在某个时刻(通过静态
initialize()
方法,可能(创建一个Data
实例,作为"选项"与类一起静态使用
例如,名为IData
和IOption
的接口将由需要此功能的类来实现。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是毫无意义的。