2010-08-17 7 views
0

Во многих моих PHP классов, у меня есть этот код:Обходной путь для множественных наследований в PHP?

private $strError = ""; 
private $intErrorCode = NULL; 
private $blnError = FALSE; 


public function isError() { 
    return $this->blnError; 
} 

public function getErrorCode() { 
    return $this->intErrorCode; 
} 

private function setError($strError, $intErrorCode = NULL) { 
    $this->blnError = TRUE; 
    $this->intErrorCode = $intErrorCode; 
    $this->strError = $strError; 
} 

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

Я хотел бы иметь двойное-расширение, где я мог бы сделать

class childClass extends parentClass, error { 
    ... 
} 

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

if ($myObject->error->isError()) {...} 

, но это не был бы также сделать его статус ошибки устанавливаемым снаружи содержащего класса,

$myObject->error->setError("I shouldn't be doing this here"); 

, который я предпочел бы избежать ?

Или я мог бы написать функции «шлюз» в вмещающем классе, которые делают соответствующие вызовы на объекте ошибок и предотвращение установки статуса ошибки из-за пределы,

class childClass extends parentClass { 

    private $error; 

    public function __construct(...) { 
     ... 
     $error = & new error(); 
     ... 
    } 

    public function isError() {...} 
    public function getError() {...} 
    public function getErrorCode() {...} 
    private function setError() {...} 

    ... 
} 

, но это приводит к (некоторые из) дублирование кода, которое я пытаюсь избежать.

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

+2

Мое предположение: вы можете сделать что-то вроде «BaseClass», из которого наследуются все родительские классы. Это не изящно, но это все, о чем я могу думать. –

ответ

3

Используйте композицию вместо наследования.

class Errors { 

    private $strError = ""; 
    private $intErrorCode = NULL; 
    private $blnError = FALSE; 


    public function isError() { 
     return $this->blnError; 
    } 

    public function getErrorCode() { 
     return $this->intErrorCode; 
    } 

    private function setError($strError, $intErrorCode = NULL) { 
     $this->blnError = TRUE; 
     $this->intErrorCode = $intErrorCode; 
     $this->strError = $strError; 
    } 

} 

И теперь использовать приватную переменную экземпляра, чтобы обратиться к нему:

class childClass extends parentClass { 
    private $errors = new Errors(); 
    ... 
} 

private Видимость мешает вам ссылки $errors вне класса.

Существует также нет необходимости создавать isError(), getError() и т. Д. Внутри childClass (и поэтому не нужно беспокоиться о дублировании кода). Просто позвоните $this->errors->isError(), $this->errors->getError() и т. Д. Если вы все же хотите, чтобы эти методы были реализованы, как показано ниже, вы можете указать интерфейс.

+2

И добавим 'interface' в' childClass', который ожидает, что 'setErrors' и' getErrors' будут реализованы. – Gordon

+1

С другой стороны, если у вас был класс ошибки, расширенный родительский класс, а все дочерние классы расширяют класс ошибок, вам не нужно будет для одного лайнера «private $ errors = new Errors();» во всех ваших дочерних классах. (Не сказать лучшего решения, просто говорю.) – Chris

+0

Я полностью смущен? Разве не вся точка * * вызывает «ошибки» вне класса? Я хочу видеть извне, если класс имеет ошибку, а $ errors - это просто способ отслеживания этого. * Только * вещь, которую я не хочу ссылаться извне класса, это 'setError()'. Я прав? Я имею в виду, что это отличный способ, чтобы класс отслеживал свои ошибки внутри себя, но в какой-то момент я хочу сообщить об этом во внешний мир, и это было в первую очередь - выяснить, если '$ myObject' находится в состоянии ошибки. – user151841

1

Вы также мог бы злоупотреблять __call магического метод, чтобы сделать то же самое:

public function __call($name, array $arguments) { 
    $name = strtolower($name); 
    if (isset($this->methods[$name])) { 
     array_unshift($arguments, $this); 
     return call_user_func_array($this->methods[$name], $arguments); 
    } 
    throw new BadMethodCallException('Method does not exist'); 
} 

Обратите внимание, что я сказал о нарушении ... В идеале, я бы придумать другую архитектуру, вместо того, все эти «общие методы "повсюду. Почему бы не использовать исключение вместо проверки $foo->isError? Если это не подходит, почему бы не украсить класс?

class Errors 
    protected $object = null; 
    public function __construct($object) { 
     $this->object = $object; 
    } 
    public function __call($method, array $arguments) { 
     $callback = array($this->object, $method); 
     if (is_callable($callback)) { 
       return call_user_func_array($callback, $arguments); 
     } 
     throw new BadMethodCallException('Method does not exist'); 
    } 
    public function __get($name) { return $this->object->$name; } 
    public function __set($name, $value) { $this->object->$name = $value; } 
    // Your methods here 
    public function isInstance($name) { return $this->object instanceof $name; } 
} 

Тогда просто "обернуть" существующий объект в этом классе:

$obj = new Errors($obj); 
$obj->foo(); 
+0

Это действительно интересный подход к этой проблеме, я могу просто попробовать и реализовать это в своем следующем проекте. Я люблю это! – Chris

+0

У этого есть некоторые действительно большие недостатки. Во-первых, вы не можете использовать интерфейсы наивно, поскольку он не будет реализовывать ваш интерфейс (следовательно, причина метода 'isInstance'). Во-вторых, отладка может быть ОЧЕНЬ сложной, так как вы не уверены в точной цепочке или порядке «декораторов» или динамических методах ... В-третьих, это может сделать для некоторых очень интересных вопросов «Где этот метод определил». Это определенно полезно в некоторых ситуациях (именно поэтому я разместил его). Но будьте очень осторожны с тем, как вы его реализуете ... – ircmaxell

1

В РНР 5.4, вы можете использовать Traits.

Например, вы могли бы сделать Черту под названием ErrorTrait так:

trait ErrorTrait { 
    private $strError = ""; 
    private $intErrorCode = NULL; 
    private $blnError = FALSE; 


    public function isError() { 
     return $this->blnError; 
    } 

    public function getErrorCode() { 
     return $this->intErrorCode; 
    } 

    private function setError($strError, $intErrorCode = NULL) { 
     $this->blnError = TRUE; 
     $this->intErrorCode = $intErrorCode; 
     $this->strError = $strError; 
    } 
} 

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

class childClass extends parentClass { 
    use ErrorTrait; 

    ... 
} 

Черты характера работают в основном как копировать/вставить, так что все из код в признаке будет доступен в классе (без дублирования кода).

Смежные вопросы