Я понимаю, что множественное наследование 1 просто не поддерживается в PHP, и хотя существует множество "хаков" или обходных путей для его эмуляции, я также понимаю, что такой подход, как состав объекта , вероятно, более гибкий, стабильный и понятный, чем такие обходные пути.Любопытно, что PHP 5,4 черты будут подходящим решением, но мы еще не совсем готовы, не так ли.
Теперь, это не просто " амидоинитрит«» вопрос, но я бы хотел, чтобы мой подход имел смысл для других.
Учитывая, что у меня есть классы Action
и Event
( есть еще, но мы будем краткими ), и они оба требуют ( около ) идентичныхметоды, очевидный подход был бы;создать общий базовый класс, расширить и перейти;В конце концов, они концептуально достаточно похожи, чтобы быть братьями и сестрами в иерархии классов ( Я думаю )
Проблема в том, что Event
необходимо расширить класс (Exception
) что само по себе ничего не может расширить.Все методы ( и свойства ) относятся к значениям «атрибута», мы будем называть их «параметры» и «данные», где «параметры» - это значения, хранящиеся на уровне класса, а «данные» - значения.хранится на уровне экземпляра.
За исключением ( нет каламбура ) класса Exception
, я могу просто создать общий класс, который расширяет все соответствующие объекты для наследования необходимой функциональности, но мне интересно, что я могу сделать, чтобы избежать, казалось бы, неизбежного дублирования кода в Event
;кроме того, другие классы, которые концептуально не достаточно похожи, чтобы быть братьями и сестрами, нуждаются в этой функциональности.
До сих пор кажется, что ответ, используя подход с композицией объектов, создать класс Data
и управлять им в двух точках:
- При создании объекта создайте экземпляр
Data
, который будет использоваться с объектом в качестве «данных». - В какой-то момент ( через статический
initialize()
возможно ) создать экземпляр Data
, который будет использоваться статически с классом в качестве "параметров".
Интерфейсы, названные, например, IData
и IOption
, будут реализованы с помощьюклассы, нуждающиеся в этой функциональности.IData
просто применяет методы экземпляра класса Data
к потребителю, и вызовы будут перенаправляться к объекту свойства экземпляра Data
, тогда как IOption
будет применять методы с аналогичными именами ( заменяет "данные" для"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);
}
// ...
}
Конечно, это всесводится к отдельным определениям для каждого класса, чтобы поддержать любое отклонение от реализации по умолчанию.
Полагаю, что в конечном итоге у меня возникают проблемы с пониманием того, где дублирование кода «в порядке» ( или, точнее, неизбежно ) и где я могу извлечь общие функции в контейнер,и как ссылаться на содержащиеся в классах функции и использовать их, отклоняясь ( обычно пренебрежимо мало ) там, где это необходимо.Опять же, черты ( в моем поверхностном тестировании на бета-версии ) кажутся здесь идеальными, но принцип композиции существует задолго до 5.4 ( и PHP полностью в этом отношении ) иЯ уверен, что для этого есть «классический» способ.
1. Интересно, что страница для множественного наследования в Википедии помечена как авторская.расследование.Алмазная проблема казалась подходящей заменой.