Согласно руководству PHP, классу как это:
abstract class Example {}
не может быть инстанцирован. Если мне нужен класс без экземпляра, например, для шаблона реестра:
class Registry {}
// and later:
echo Registry::$someValue;
это считали бы хорошим стилем для простого объявления класса как краткого обзора? В противном случае, что преимуществами сокрытия является конструктор как защищенный метод по сравнению с абстрактным классом?
Объяснение для выяснения: Насколько я вижу его, это могло немного злоупотребления функции, так как руководство относится к абстрактным классам больше так же как проекты более поздних классов с возможностью инстанцирования.
Обновление: В первую очередь, спасибо за все ответы! Но много ответов звучат довольно подобными: 'Вы не можете инстанцировать абстрактного класса, но для реестра, почему, не используя шаблон "одиночка"?'
К сожалению, это было более или менее точно повторением моего вопроса. Что преимуществом использования является шаблон "одиночка" (иначе скрывающийся __construct()
) по сравнению только с объявлением его abstract
и не имея необходимость волноваться об этом? (Как, например, это - сильная коннотация между разработчиками, этим abstract
классы на самом деле не используются или около этого.)
Если ваш класс не предназначен для определения какого-то супертипа, его не следует объявлять как абстрактный
, Я бы сказал.
В вашем случае я бы предпочел использовать класс:
__ construct
и __ clone
как частные методы
Итак, зачем использовать синглтоны, а не только статические методы? Я полагаю, что, по крайней мере, может быть несколько причин:
__ construct
и __ clone
частными и добавить некоторый метод getInstance
. $ this
, свойства, ... __ callStatic
был введен только в PHP 5.3 __ getStatic
, __ setStatic
, ...
При этом, да, такой код:
abstract class MyClass {
protected static $data;
public static function setA($a) {
self::$data['a'] = $a;
}
public static function getA() {
return self::$data['a'];
}
}
MyClass::setA(20);
var_dump(MyClass::getA());
Будет работать ... Но это не совсем естественно ... и это очень простой пример (см. То, что я сказал ранее с Позднее статическое связывание и магические методы) .
Как сказали другие ребята, вы не можете инстанцировать абстрактный класс. Вы можете использовать статические методы в вашем классе, чтобы предотвратить инстанцирование, но я не очень люблю это делать, если у меня нет веской причины.
Возможно, я немного отклоняюсь от темы, но в вашем примере вы сказали, что это нужно для класса с шаблоном реестра. По какой причине вы не хотите его инстанцировать? Не лучше ли создать экземпляр Registry для каждого регистра, который вы хотите использовать?
Что-то вроде:
class Registry {
private $_objects = array( );
public function set( $name, $object ) {
$this->_objects[ $name ] = $object;
}
public function get( $name ) {
return $this->_objects[ $name ];
}
}
Я бы даже не стал использовать Singleton в этом случае.
abstract
действительно предназначен для обозначения "чертежа", как вы это называете, для наследования класса.
Реестры обычно следуют шаблону singleton, что означает, что они будут инстанцировать себя в частной переменной. Определение его как абстрактного
не позволит этому работать.
Я бы не стал использовать абстрактный класс. Я бы использовал что-то похожее на синглтон с защищенным/частным конструктором, как вы предлагаете. Должно быть очень мало статических свойств, кроме $instance
, который является фактическим экземпляром реестра. В последнее время я стал поклонником типичного паттерна Zend Frameworks, который выглядит примерно так:
class MyRegistry {
protected static $instance = null;
public function __construct($options = null)
{
}
public static function setInstance(MyRegistry $instance)
{
self::$instance = $instance;
}
public static function getInstance()
{
if(null === self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
}
Таким образом, вы получаете по сути синглтон, но можете вводить настроенный экземпляр для использования. Это удобно для целей тестирования и наследования.
Цель абстрактного класса - определить методы, которые 1) имеют смысл для других классов и 2) не имеют смысла, когда не находятся в контексте одного из этих классов.
Перефразируя некоторые документы по php, представьте, что вы подключаетесь к базе данных. Подключение к базе данных не имеет особого смысла, если у вас нет определенного типа базы данных, к которой нужно подключиться. Тем не менее, подключение - это то, что вы захотите сделать независимо от типа базы данных. Поэтому подключение может быть определено в абстрактном классе базы данных и унаследовано, а также наполнено смыслом, скажем, в классе MYSQL.
Судя по вашим требованиям, похоже, что вы не собираетесь этого делать, а просто требуете класс без экземпляра. Хотя вы можете использовать абстрактный класс для обеспечения такого поведения, это кажется мне халтурой, потому что это злоупотребляет назначением абстрактных классов. Если я встречаю абстрактный класс, я должен быть в состоянии разумно ожидать, что он будет иметь некоторые абстрактные методы, например, но ваш класс не будет иметь их.
Поэтому синглтон кажется лучшим вариантом.
Однако, если причина, по которой вы хотите иметь класс без экземпляра, просто в том, что вы можете вызвать его в любом месте, тогда зачем вообще нужен класс? Почему бы просто не загрузить каждую переменную как глобальную, и тогда вы сможете вызывать ее напрямую, а не через класс?
Я думаю, что лучший способ сделать это - инстанцировать класс и затем передавать его с помощью инъекции зависимостей. Если вы слишком ленивы для этого (и это справедливо! Это ваш код, не мой), то не заморачивайтесь с классом вообще.
UPDATE: Похоже, что у вас конфликт между двумя потребностями: необходимостью делать все быстро и необходимостью делать все правильно. Если вам не важно иметь тонну глобальных переменных ради экономии времени, вы предположительно предпочтете использовать абстракцию вместо синглтона, потому что она предполагает меньшее количество типов. Выберите, какая потребность для вас важнее, и придерживайтесь ее.
Правильный путь здесь - определенно не использовать синглтон или абстрактный класс, а вместо этого использовать инъекцию зависимостей. Быстрый способ - иметь тонну глобалов или абстрактный класс.
То, что вы описываете, разрешено языком PHP, но это не предполагаемое использование абстрактного
класса. Я бы не стал использовать статические
методы абстрактного
класса.
У этого есть обратная сторона: другой разработчик может расширить ваш абстрактный класс, а затем создать экземпляр объекта, чего вы хотите избежать. Пример:
class MyRegistry extends AbstractRegistry { }
$reg = new MyRegistry();
Верно, вам нужно беспокоиться об этом только в том случае, если вы передаете свой абстрактный класс другому разработчику, который не соответствует вашему предполагаемому использованию, но именно поэтому вы должны сделать класс одноэлементным. Не склонный к сотрудничеству разработчик может переопределить частный конструктор:
class Registry
{
private function __construct() { }
}
class MyRegistry extends Registry
{
public function __construct() { } // change private to public
}
Если бы вы сами использовали этот класс, вы бы просто запомнили , а не для создания экземпляра класса. Тогда вам не понадобится ни один механизм, чтобы предотвратить это. Итак, поскольку вы разрабатываете это для использования другими, вам нужен какой-то способ не дать этим людям обойти ваше предполагаемое использование.
Итак, я предлагаю эти две возможные альтернативы:
Придерживайтесь одноэлементного шаблона и убедитесь, что конструктор также является final
, чтобы никто не мог расширить ваш класс и изменить конструктор на неприкосновенный:
class Registry
{
private final function __construct () {
}
}
Сделайте так, чтобы ваш реестр поддерживал как Использование статических и объектных объектов:
Реестр классов
{
protected static $ reg = null;
общедоступная статическая функция getInstance () {{{ 1}} if (self :: $ reg === null) {
self :: $ reg = new Registry ();
}
return self :: $ reg;
}
}
Затем вы можете вызвать Registry :: getInstance ()
статически, или вы можете вызвать new Registry ()
, если вам нужен экземпляр объекта.
Затем вы можете делать изящные вещи, например сохранять новый экземпляр реестра в глобальном реестре! : -)
Я реализовал это как часть Zend Framework в Zend_Registry
Установка класса для абстрагирования, что только определение статических свойств / методов не будет иметь реального эффекта. Вы можете расширить класс, создать его экземпляр и вызвать для него метод, который изменит свойства статического класса. Очевидно, очень запутанно.
Аннотация также вводит в заблуждение. Аннотация - это определение класса, который реализует некоторые функции, но требует большего поведения (добавленного через наследование) для правильного функционирования. Вдобавок ко всему, это обычно функция, которую вообще не следует использовать со статикой. Вы практически приглашаете программистов использовать его неправильно.
Краткий ответ: частный конструктор был бы более выразительным и отказоустойчивым.
Насколько я понимаю, класс без экземпляра - это то, что вы не должны использовать в ООП программа, потому что вся (и единственная) цель классов - служить схемами для новых объектов. Единственное различие между Registry :: $ someValue
и $ GLOBALS ['Registry_someValue']
состоит в том, что первый выглядит «более привлекательно», но ни один из них не является объектно-ориентированным.
Итак, чтобы ответить на ваш вопрос, вам не нужен «одноэлементный класс», вам нужен одноэлементный объект, необязательно оснащенный фабричным методом:
class Registry
{
static $obj = null;
protected function __construct() {
...
}
static function object() {
return self::$obj ? self::$obj : self::$obj = new self;
}
}
...
Registry::object()->someValue;
Очевидно, abstract
здесь не будет работать .
В объектно-ориентированном пространстве есть шаблоны, которые являются общими и хорошо распознаваемыми. Использование abstract
нетрадиционным способом может вызвать путаницу (извините, некоторые мои примеры написаны на Java, а не на PHP):
abstract
в классе, чтобы предотвратить прямое создание экземпляра, но разрешить наследование от класса final
для класса, а final
для класса, а getInstance ()
), который возвращает единственный экземпляр или один из ограниченного числа экземпляров Я бы сказал, что это вопрос привычек кодирования . Когда вы думаете об абстрактном классе, обычно вам нужно создать подкласс для использования. Поэтому объявление вашего абстрактного класса противоречит интуиции.
В остальном это просто вопрос использования self :: $ somevar в ваших методах, если вы делаете его абстрактным, а не $ this-> somevar, если вы реализуете его как синглтон.