В PHP, как я могу рекурсивно вызвать метод сверху вниз по иерархии из метода в самом старшем родителе? - PullRequest
1 голос
/ 20 сентября 2011

У меня есть класс, который расширен, и его потомки расширены, произвольное количество раз. Каждое расширение предоставляет больше функциональных возможностей, чем его предшественник. Суть в том, что не все параметры могут быть предоставлены во время инициализации. Это достаточно сложно, чтобы гарантировать передачу дополнительных параметров конфигурации через методы, а затем вызвать какой-то метод build () или configure (), чтобы подготовить себя к работе.

Смысл в том, что каждому классу в иерархии нужен шанс настроить себя, и он должен каскадно перейти от родителя ко всем дочерним элементам.

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

Как мне изменить этот код для достижения этого?

<?php

class A {
    function configure() {
        print("I am A::configure() and provide basic functionality.<br/>\n");;
    }
}

class B extends A {
    function configure() {
        parent::configure();
        print("I am B::configure() and provide additional functionality.<br/>\n");
    }
}

class C extends B {
    function configure() {
        parent::configure();
        print("I am C::configure() and provide even more functionality.<br/>\n");
    }
}

$c = new C;
$c->configure();

?>

Выход:

I am A::configure() and provide basic functionality.
I am B::configure() and provide additional functionality.
I am C::configure() and provide even more functionality.

Спасибо за вашу помощь! :)

Ответы [ 3 ]

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

Не утверждая, что это красиво, я бы предложил следующее.Для краткости я оставил ваши функции configure ().

<?php

class A {
  function configure_master() {
    $init_class = get_class($this);
    $ancestry = array();
    while (! empty($init_class)) {
      $ancestry[] = $init_class;
      $init_class = get_parent_class($init_class);
    }
    for ($i = count($ancestry) - 1; $i >= 0; $i--) {
      call_user_func(array($this, $ancestry[$i] . '::configure'));
    }
  }
}

$c = new C;
$c->configure_master();

Я проверил это, и оно работает.call_user_func($ancestry[$i] . '::configure') также работает (по крайней мере, в php 5.3, где я его тестировал), но он полагается на странную (для меня) область видимости $ this.Я не уверен, является ли это надежным или будет продолжено в будущих версиях.Вышеуказанная версия «должна быть» более надежной, учитывая мои ограниченные знания.

Если требуется поддерживать внешний вызов как configure (), я бы предложил переименовать все ваши методы «configure ()» в «configure_local»() или что-то подобное, и переименуйте «configure_master ()» в «configure ()».На самом деле, это то, что я хотел бы сделать для начала, но это вопрос личного вкуса.

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

1 голос
/ 20 сентября 2011

Произвольное количество наследства?Я не могу сказать, что это путь, по которому я бы хотел пойти, но если вы должны это сделать, вы можете написать некоторый тип метода настройки в базовом классе, который принимает вызывающий (дочерний) класс в качестве параметра.Затем этот метод будет определять тип класса, обходить дерево наследования (помещая каждый родительский класс в стек), а затем просто последовательно запускать каждый метод configure.Я думаю, что основной принцип можно назвать рефлексией, но я не уверен.(Отредактируйте: теперь еще менее уверен. Обязательно прочитайте об этом, прежде чем поверить мне на слово.)

Метод в базовом классе, возможно, должен быть статическим, хотя ...

Я считаю, чточто-то вроде этого было бы возможно, но я не очень разбираюсь в PHP.Я немного поищу и посмотрю, позволяет ли это синтаксис и код. Надеюсь, однако, что вы можете использовать эту идею.

Это ужасная форма псевдокода моей идеи.

<?php

class A {
    static function configure(class) {
        //get qualified class name from argument
        //get inheritance tree for qualified class name, push each tier into array
        //loop: pop array, call configure method for each class in hierarchy

        //this might possibly work?
    }
}

class B extends A {
    function configure() {
        print("I am B::configure() and provide additional functionality.<br/>\n");
    }
}

class C extends B {
    function configure() {
        print("I am C::configure() and provide additional functionality.<br/>\n");
    }
}

$c = new C;
A::configure($c);

?>

Опять же, я посмотрю и смогу ли что-нибудь в PHP поддержать эту теорию. А пока я могу жить с несколькими отрицательными голосами.

Редактировать: На самом деле, это может не произойтидолжны быть статичными, если имена методов не перекрываются во время наследования. Это может быть немного более элегантно, поскольку объект может найти свое собственное имя класса и иерархию, вызвав метод master configure.

0 голосов
/ 20 сентября 2011

Как насчет этого?(Простите мой синтаксис PHP, если он несовершенен)

<?php
class MyBase {
   private $_isChild = true;

   public function __construct()
       $this->_isChild = false;
   }

   public function behave() {
       if $this->_isChild {
           parent::behave();
       }
       self::doBehave();
   }

   protected function doBehave()
       // do stuff here!
   }
}
?>

В ваших дочерних классах просто реализуйте doBehave.

Max

...