Я много читал о конструкторах и инициализирующих переменных и столкнулся с проблемой, которую пытаюсь решить. Я пытаюсь решить проблему отсутствия поддержки обобщений путем введения переменной, которая должна быть инициализирована подклассом.
<?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;
}
}
?>
Мне показалось, что я распознал это как анти-шаблон, поэтому я искал, где я об этом читал, но потом я вспомнил, что это было так: 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';
}
}
Используйте шаблон Factory, чтобы скрыть детали конструктора … см. http://www.phptherightway.com/pages/Design-Patterns.html
Делать 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
«имущество».
РЕДАКТИРОВАТЬ:
Использование фабрики, как предлагает Лука Рокки, также очень хорошая идея.