我知道多重继承1在PHP中不被支持,虽然有许多“hack”或变通方法可以模拟它,但我也知道像对象组合这样的方法可能比这些变通方法更加灵活、稳定和易懂。有趣的是,PHP 5.4的traits将成为适当的解决方案,但我们还没有完全到达那里。
现在,这不仅仅是一个“amidoinitrite?”的问题,而是我想确保我的方法对其他人来说是有意义的。
假设我有类
问题是
除了
到目前为止,答案似乎是使用对象组合方法,创建一个
编辑:或者,我可以将对于
当然,这一切都归结于每个类的单独定义,以支持任何与默认实现不同的偏差。
我想最终问题在于,我很难理解何时代码重复是“可以”(更准确地说,是不可避免的),何时可以将常用功能提取到容器中,并如何跨类引用和使用所包含的功能,在必要时进行微小的偏差。再次强调,特质(在我的beta测试中)似乎非常适合这里,但组成原则早在5.4之前就存在(对于PHP来说也是如此),我确信有一种“经典”的方法可以实现这一点。
现在,这不仅仅是一个“amidoinitrite?”的问题,而是我想确保我的方法对其他人来说是有意义的。
假设我有类
Action
和Event
(还有更多,但我们会保持简洁),它们都需要非常相似的方法,显然的方法是创建一个共同的基类,进行扩展;毕竟,它们在概念上足够相似,可以构成类层次结构中的兄弟姐妹(我想)。问题是
Event
需要扩展一个类(Exception
),但该类本身不能扩展任何内容。这些方法(和属性)都与"属性"值有关,我们将它们称为"选项"和"数据",其中"选项"是存储在类级别上的值,而"数据"是存储在实例级别上的值。除了
Exception
类(无意中的双关语),我可以简单地创建一个通用类,让所有相关对象扩展以继承必要的功能,但我想知道如何避免在Event
中似乎不可避免的代码重复;此外,其他概念上不足以成为兄弟的类也需要这个功能。到目前为止,答案似乎是使用对象组合方法,创建一个
Data
类,并在两个点上管理它:
- 在对象实例化时,创建一个
Data
实例作为对象使用的"data"。 - 在某个时间点(可能通过静态
initialize()
方法),创建一个Data
实例作为类使用的"options"。
例如,名为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
的引用(无论何时需要)设为公共,从而消除代理方法的需求,简化组合。然而问题是,如果我需要使getData()
像这个代码片段所示那样递归地工作,那么我就不能偏离Data
类的功能性。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);
}
// ...
}
当然,这一切都归结于每个类的单独定义,以支持任何与默认实现不同的偏差。
我想最终问题在于,我很难理解何时代码重复是“可以”(更准确地说,是不可避免的),何时可以将常用功能提取到容器中,并如何跨类引用和使用所包含的功能,在必要时进行微小的偏差。再次强调,特质(在我的beta测试中)似乎非常适合这里,但组成原则早在5.4之前就存在(对于PHP来说也是如此),我确信有一种“经典”的方法可以实现这一点。
有趣的是,维基百科上 多重继承 的页面已被标记为版权调查。钻石问题似乎是一个合适的替代品。