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

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

<?php

abstract class Collection_Base {
protected $typeOf; // Must be initialised by the subclass
protected $collection = array();
}

class Cookie_Collection extends Collection_Base {
protected $typeOf = 'System\Web\Http_Cookie';

public function set ($item) {
if (!$item instanceof $this->typeOf) {
throw new \InvalidArgumentException;
}
$this->collection[] = $item;
}
}

?>

Так что мне было интересно, это плохая практика включать переменную, которая должна быть инициализирована конструктором подкласса в PHP? Есть ли что-то, что мне нужно знать при этом?

Хотя это не имеет прямого отношения, я использовал следующие источники для сбора информации:


РЕШЕНИЕ

<?php

abstract class Collection_Base {
protected $collection = array();

public abstract function getType();

private function getTypeInternal () {
$type = $this->getType();

if (is_class($type)) {
throw new \UnexpectedValueException;
}

return $type;
}

public function set ($item) {
$type = $this->getTypeInternal();
if (!$item instanceof $type) {
throw new \InvalidArgumentException;
}
$this->collection[] = $item;
}
}

class Cookie_Collection extends Collection_Base {
protected $type = 'System\Web\Http_Cookie';

public function getType () {
return $this->type;
}
}

?>

2

Решение

Мне показалось, что я распознал это как анти-шаблон, поэтому я искал, где я об этом читал, но потом я вспомнил, что это было так: http://en.wikipedia.org/wiki/Call_super, что не совсем то же самое.

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

abstract class Collection_Base {
protected $typeOf;

protected $collection = array();

/**
* @return string
*/
public function getType()
{
if (null === $this->typeOf) {
$this->typeOf = $this->doGetType();
}

return $this->typeOf;
}

/**
* @return string
*/
abstract protected function doGetType();
}

class Cookie_Collection extends Collection_Base {

/**
* @param $item
*/
public function set ($item) {
if (!$item instanceof $this->getType()) {
throw new \InvalidArgumentException;
}
$this->collection[] = $item;
}

/**
* @inheritdoc
*/
protected function doGetType()
{
return 'System\Configuration\Configuration_Element';
}
}
1

Другие решения

Используйте шаблон Factory, чтобы скрыть детали конструктора … см. http://www.phptherightway.com/pages/Design-Patterns.html

1

Делать Collection_Base абстрактный класс и определите метод, который возвращает соответствующее имя класса:

abstract class Collection_Base
{
protected $collection = [];

public function add($item)
{
if (!$item instanceof $this->getType()) {
throw new \InvalidArgumentException();
}

$this->collection[] = $item;
}

abstract protected function getType();
}

class Collection_Cookie extends Collection_Base
{
protected function getType()
{
return Configuration_Element::class;
}
}

Используя этот подход, другие разработчики не могут забыть о type «имущество».


РЕДАКТИРОВАТЬ:

Использование фабрики, как предлагает Лука Рокки, также очень хорошая идея.

1