Множественное наследование класс mixin


Я написал простой класс, который немного имитирует множественное наследование через миксины. Это позволяет расширить функциональность нескольких классов и управлять любыми конфликтами между ними, если они возникнут.

Смеситель класса

abstract class Vm_Mixer {

    protected $methods = array();
    protected $mixins = array();
    protected $priorities = array();

    /**
     * @description By adding a mixin, the class will automatically adopt all of a mixin's methods.
     * @param object $mixin - The instantiated object that's methods should be adopted by the extending class.
     */
    public function addMixin($mixin){
        $name = get_class($mixin);
        $this->mixins[$name] = $mixin;
        $methods = get_class_methods($name);
        $this->methods[$name] = $methods;
    }

    /**
     * @description Gets the class's current mixins by name
     * @return An array of mixin names
     */
    public function getMixins(){
        return array_keys($this->methods);
    }

    /**
     * @description Manages conflicts for the mixins.
     * @param array $priorities - The method name as the key, the class name that has priority in a conflict as the value.
     */
    public function setPriorities(array $priorities){
        $this->priorities = $priorities;
    }

    /**
     * @description Magic method that calls the mixin methods automatically
     * @param string $methodName - The name of the mixin method
     * @param array $arguments - The arguments for the method
     */
    public function __call($methodName, $arguments){
        foreach ($this->methods as $className=>$methods){
            if (in_array($methodName, $methods)){
                if ((in_array($methodName, array_keys($this->priorities)))&&($className == $this->priorities[$methodName])){
                    call_user_func_array(array($className, $methodName), $arguments);
                    break;
                } else if (!in_array($methodName, array_keys($this->priorities))){
                    call_user_func_array(array($className, $methodName), $arguments);
                    break;                  
                }
            } 
        }
    }
}

Пример

Вот небольшой пример, как это может быть использовано:

Подмешать 1

class Weapons {

    public function laser(){
        echo 'Firing laser!<br/>';
    }

    public function catapult(){
        echo 'Launching catapult! MEEEOOOWWW!<br/>';
    }

    public function explode(){
        echo 'Boom!<br/>';
    }

    public function greet(){
        echo 'Prepare for your doom!<br/>';
    }
}

Подмешать 2

class Defense {

    public function shield(){
        echo 'Enabling shield!<br/>';
    }

    public function invisibility(){
        echo 'Activating invisibility cloak!<br/>';
    }

    public function explode(){
        echo 'Self destruct sequence activated!<br/>';
    }

    public function serve(array $foodItems){
        echo '#Sets table with '.implode(', ', $foodItems).'#<br/>';
    }
}

Робот класса

class Robot extends Vm_Mixer {

    protected $name;

    public function __construct($name){
        $this->name = $name;
        $this->addMixin(new Weapons());
        $this->addMixin(new Defense());

        //The explode method has a conflict as it exists in both Weapons and Defense, so let's give precedence to Defense
        $this->setPriorities(array('explode'=>'Defense'));
    }

    //Pre-empts the Weapons::greet method
    public function greet(){
        echo "Hi, my name is $this->name.</br>";
    }
}

Использование

$robot = new Robot('Robby');
$robot->greet();
$robot->shield();
$robot->laser();
$robot->explode();
$robot->serve(array('Pizza', 'Cheeseburgers', 'Egg Nog', 'Vanilla Pudding'));

Выше будет возвращать:

Hi, my name is Robby.  
Enabling shield!  
Firing laser!  
Self destruct sequence activated! 
#Sets table with Pizza, Cheeseburgers, Egg Nog, Vanilla Pudding#

Что я ищу в комментарий следующим образом:

  • Сколько будет производительность возникнуть проблема с этой реализации (я в основном про метод перегрузки здесь)?
  • Есть любые другие вопросы, которые я мог бы столкнуться, что может вызвать конфликты или проблемы?
  • Как я могу улучшить существующий код?
  • Каким образом это может быть использовано? Я знаю несколько, но других идей, было бы хорошо.
  • Когда нужно и не нужно что-то вроде этого?


3558
5
задан 26 ноября 2011 в 09:11 Источник Поделиться
Комментарии
1 ответ

Это интересный подход и он работает, как ожидалось. Моя первая реакция при чтении кода, что вы пытаетесь решить задачу, которая обычно решается с составной шаблон, но потом понял, что вы на самом деле не пытается решить проблему, но и представить идею.

Теперь что касается конкретики:


Сколько будет производительность возникнуть проблема с этой реализации (я в основном про метод перегрузки здесь)?

Если вы не найдете реалистичный сценарий, вы не можете обсуждать производительность. Обычно перегрузка метода и отражение в API считаются очень медленно, но спектакль не о медлительности, а о сравнении решений. Если мы не найдем реалистичное использование вашей архитектуры, и, по крайней мере, одна альтернатива, производительность не имеет значения.


Есть любые другие вопросы, которые я мог бы столкнуться, что может вызвать конфликты или проблемы?

Вы имеете дело с Алмазный проблема с приоритетами программы, но вы не делаете ничего для случаев равный приоритет. Это может быть за пределами вашей области, но в следующем фрагменте кода показано, что опираясь на приоритетный список крайне уязвима:

$robot->setPriorities(array('explode'=>'Defense'));
$robot->setPriorities(array('explode'=>'Weapons'));
$robot->setPriorities(array('explode'=>'Defense'));
$robot->setPriorities(array('explode'=>'Weapons'));

В любой момент список приоритетов может быть изменен, и последний будет фактической. Такое динамическое поведение может привести к мириаде крайне трудно найти ошибки, вы должны рассмотреть возможность исключения в случае, если приоритет уже был установлен.

Кроме того, вы не проверять приоритетные действия и вы могли бы сделать что-то вроде:

$robot->setPriorities(array('explode'=>'aaaa'));

поскольку вы не проверить, если класс приоритета существует на самом деле и установить приоритет в любом случае, которое будет оказывать взорваться() uncallable. И, конечно, вы могли бы сделать что-то злое, как:

$robot->setPriorities(array('explode'=>'Robot'));

что бы бросить своего робота в бесконечном цикле. Так вы еще не разобрались со всеми аспектами проблемы бриллиант. Вы должны также проверить, если класс существует в addMixin().


Как я могу улучшить существующий код?

Здесь:

public function __call($methodName, $arguments){
foreach ($this->methods as $className=>$methods){
if (in_array($methodName, $methods)){
if ((in_array($methodName, array_keys($this->priorities)))&&($className == $this->priorities[$methodName])){
call_user_func_array(array($className, $methodName), $arguments);
break;
} else if (!in_array($methodName, array_keys($this->priorities))){
call_user_func_array(array($className, $methodName), $arguments);
break;
}
}
}
}


  • было бы намного приятнее, если бы вы вернули стоимость "миксина" класса, а не просто назвав его и ломать,

  • вы должны заниматься делом, не существующие функции,

  • блок вроде не читается.

Я бы рефакторинг, как:

public function __call($methodName, $arguments){
foreach ($this->methods as $className => $methods) {
if(!in_array($methodName, $methods)) {
continue;
}

if(
in_array($methodName, array_keys($this->priorities))
&& $className == $this->priorities[$methodName]
) {
return call_user_func_array(array($className, $methodName), $arguments);
} else if (!in_array($methodName, array_keys($this->priorities))) {
return call_user_func_array(array($className, $methodName), $arguments);
}
}

throw new Exception("Function not found"); // or something like that
}

И, наверное, необходимо как-то бороться с исключения из названных функций.


Каким образом это может быть использовано? Я знаю несколько, но других идей, было бы хорошо.

Когда нужно и не нужно что-то вроде этого?

Я абсолютно не знаю. Я работал над собой, чтобы забыть все про множественное наследование, так как мои корни программирования в C++. Было бы неплохо, если вы расскажете нам о своих идеях, что заставило вас придумать подход.

В PHP 5.4 будет поддерживать черты, концепция очень похожа на миксины. Так что, к сожалению, Ваш подход скоро станет устаревшим.

4
ответ дан 26 ноября 2011 в 08:11 Источник Поделиться