PHP и множественное наследование;Я знаю, что ты не можешь, но тогда как мне ..? - PullRequest
3 голосов
/ 05 ноября 2011

Я понимаю, что множественное наследование 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. Интересно, что страница для множественного наследования в Википедии помечена как авторская.расследование.Алмазная проблема казалась подходящей заменой.

1 Ответ

1 голос
/ 06 ноября 2011

РЕДАКТИРОВАТЬ: Я только что прочитал ваш вопрос еще раз, и вы, кажется, предлагаете, чтобы вы фактически использовали методы получения и установки для манипулирования данными. Если это так, то не могли бы вы предоставить мне более подробную информацию о том, чего вы пытаетесь достичь. Я подозреваю, что то, как вы решили смоделировать свои объекты и данные, привело вас к этой ситуации, и что альтернатива решит проблему.

Вам не нужно множественное наследование. Вам даже не понадобится большая часть написанного вами кода.

Если целью классов 'Data' и 'Option' является простое хранение данных, тогда используйте массив. Или, если вы предпочитаете синтаксис объекта, приведите массив к объекту или экземпляру stdClass:

$person = (object)array(
    'name' => 'Peter',
    'gender' => 'Male'
);

OR

$person = new stdClass;
$person->name = 'Peter';
$person->gender = 'Male';

Наличие целой группы методов получения и установки, которые фактически ничего не делают с данными, бессмысленно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...