Статические классы в PHP через абстрактное ключевое слово?

Согласно руководству PHP, классу как это:

abstract class Example {}

не может быть инстанцирован. Если мне нужен класс без экземпляра, например, для шаблона реестра:

class Registry {}
// and later:
echo Registry::$someValue;

это считали бы хорошим стилем для простого объявления класса как краткого обзора? В противном случае, что преимуществами сокрытия является конструктор как защищенный метод по сравнению с абстрактным классом?

Объяснение для выяснения: Насколько я вижу его, это могло немного злоупотребления функции, так как руководство относится к абстрактным классам больше так же как проекты более поздних классов с возможностью инстанцирования.

Обновление: В первую очередь, спасибо за все ответы! Но много ответов звучат довольно подобными: 'Вы не можете инстанцировать абстрактного класса, но для реестра, почему, не используя шаблон "одиночка"?'

К сожалению, это было более или менее точно повторением моего вопроса. Что преимуществом использования является шаблон "одиночка" (иначе скрывающийся __construct()) по сравнению только с объявлением его abstract и не имея необходимость волноваться об этом? (Как, например, это - сильная коннотация между разработчиками, этим abstract классы на самом деле не используются или около этого.)

21
задан Boldewyn 23 March 2010 в 07:50
поделиться

10 ответов

Если ваш класс не предназначен для определения какого-то супертипа, его не следует объявлять как абстрактный , Я бы сказал.

В вашем случае я бы предпочел использовать класс:

  • , который определяет __ construct и __ clone как частные методы
    • , поэтому класс не может быть создан извне
  • И, таким образом, ваш класс может создать экземпляр самого себя


Итак, зачем использовать синглтоны, а не только статические методы? Я полагаю, что, по крайней мере, может быть несколько причин:

  • Использование синглтона означает использование экземпляра класса; упрощает преобразование не-одноэлементного класса в одноэлементный: нужно только сделать __ construct и __ clone частными и добавить некоторый метод getInstance .
  • Использование синглтона также означает, что у вас есть доступ ко всему, что вы можете использовать с обычным экземпляром: $ this , свойства, ...
  • О, третий (не уверен насчет это, но может иметь свое значение) : с PHP <5.3, у вас меньше возможностей со статическими методами / данными:
  • Позднее статическое связывание было добавлено только в PHP 5.3; и его отсутствие часто усложняет работу со статическими методами / классами; особенно при использовании наследования.


При этом, да, такой код:

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());

Будет работать ... Но это не совсем естественно ... и это очень простой пример (см. То, что я сказал ранее с Позднее статическое связывание и магические методы) .

19
ответ дан 29 November 2019 в 21:41
поделиться

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

Возможно, я немного отклоняюсь от темы, но в вашем примере вы сказали, что это нужно для класса с шаблоном реестра. По какой причине вы не хотите его инстанцировать? Не лучше ли создать экземпляр Registry для каждого регистра, который вы хотите использовать?

Что-то вроде:

class Registry {
    private $_objects = array( );

    public function set( $name, $object ) {
        $this->_objects[ $name ] = $object;
    }

    public function get( $name ) {
        return $this->_objects[ $name ];
    }
}

Я бы даже не стал использовать Singleton в этом случае.

1
ответ дан 29 November 2019 в 21:41
поделиться

abstract действительно предназначен для обозначения "чертежа", как вы это называете, для наследования класса.

Реестры обычно следуют шаблону singleton, что означает, что они будут инстанцировать себя в частной переменной. Определение его как абстрактного не позволит этому работать.

0
ответ дан 29 November 2019 в 21:41
поделиться

Я бы не стал использовать абстрактный класс. Я бы использовал что-то похожее на синглтон с защищенным/частным конструктором, как вы предлагаете. Должно быть очень мало статических свойств, кроме $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;
  }
}

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

0
ответ дан 29 November 2019 в 21:41
поделиться

Цель абстрактного класса - определить методы, которые 1) имеют смысл для других классов и 2) не имеют смысла, когда не находятся в контексте одного из этих классов.

Перефразируя некоторые документы по php, представьте, что вы подключаетесь к базе данных. Подключение к базе данных не имеет особого смысла, если у вас нет определенного типа базы данных, к которой нужно подключиться. Тем не менее, подключение - это то, что вы захотите сделать независимо от типа базы данных. Поэтому подключение может быть определено в абстрактном классе базы данных и унаследовано, а также наполнено смыслом, скажем, в классе MYSQL.

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

Поэтому синглтон кажется лучшим вариантом.

Однако, если причина, по которой вы хотите иметь класс без экземпляра, просто в том, что вы можете вызвать его в любом месте, тогда зачем вообще нужен класс? Почему бы просто не загрузить каждую переменную как глобальную, и тогда вы сможете вызывать ее напрямую, а не через класс?

Я думаю, что лучший способ сделать это - инстанцировать класс и затем передавать его с помощью инъекции зависимостей. Если вы слишком ленивы для этого (и это справедливо! Это ваш код, не мой), то не заморачивайтесь с классом вообще.

UPDATE: Похоже, что у вас конфликт между двумя потребностями: необходимостью делать все быстро и необходимостью делать все правильно. Если вам не важно иметь тонну глобальных переменных ради экономии времени, вы предположительно предпочтете использовать абстракцию вместо синглтона, потому что она предполагает меньшее количество типов. Выберите, какая потребность для вас важнее, и придерживайтесь ее.

Правильный путь здесь - определенно не использовать синглтон или абстрактный класс, а вместо этого использовать инъекцию зависимостей. Быстрый способ - иметь тонну глобалов или абстрактный класс.

0
ответ дан 29 November 2019 в 21:41
поделиться

То, что вы описываете, разрешено языком PHP, но это не предполагаемое использование абстрактного класса. Я бы не стал использовать статические методы абстрактного класса.

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

class MyRegistry extends AbstractRegistry { }
$reg = new MyRegistry();

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

class Registry
{
  private function __construct() { }
}

class MyRegistry extends Registry
{
  public function __construct() { } // change private to public
}

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

Итак, я предлагаю эти две возможные альтернативы:

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

     class Registry 
     {
    private final function __construct () {
    } 
    } 
     
  2. Сделайте так, чтобы ваш реестр поддерживал как Использование статических и объектных объектов:

     Реестр классов 
     {
    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

3
ответ дан 29 November 2019 в 21:41
поделиться

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

Аннотация также вводит в заблуждение. Аннотация - это определение класса, который реализует некоторые функции, но требует большего поведения (добавленного через наследование) для правильного функционирования. Вдобавок ко всему, это обычно функция, которую вообще не следует использовать со статикой. Вы практически приглашаете программистов использовать его неправильно.

Краткий ответ: частный конструктор был бы более выразительным и отказоустойчивым.

1
ответ дан 29 November 2019 в 21:41
поделиться

Насколько я понимаю, класс без экземпляра - это то, что вы не должны использовать в ООП программа, потому что вся (и единственная) цель классов - служить схемами для новых объектов. Единственное различие между 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 здесь не будет работать .

0
ответ дан 29 November 2019 в 21:41
поделиться

В объектно-ориентированном пространстве есть шаблоны, которые являются общими и хорошо распознаваемыми. Использование abstract нетрадиционным способом может вызвать путаницу (извините, некоторые мои примеры написаны на Java, а не на PHP):

  • абстрактный класс - класс, предназначенный для концептуализации общего предка, но из которого фактические экземпляры не предназначены для существования (например, shape является абстрактным суперклассом для прямоугольника и треугольника).
    Обычно реализуется:
    • использовать модификатор abstract в классе, чтобы предотвратить прямое создание экземпляра, но разрешить наследование от класса
  • служебный класс - класс, который не представляет объект в пространство решений, а скорее набор полезных статических операций / методов, например Математический класс на Java.
    Обычно реализуется с помощью:
    • сделать класс невыводимым, например Java использует модификатор final для класса, а
    • предотвращает прямое создание экземпляров - не предоставляет конструкторов и скрывает или отключает любые неявные конструкторы или конструкторы по умолчанию (и конструкторы копирования)
  • одноэлементный класс - класс который действительно представляет объект в пространстве решений, но чье создание экземпляра контролируется или ограничивается, часто чтобы гарантировать, что существует только один экземпляр.
    Обычно реализуется:
    • делает класс невыводимым, напримерJava использует модификатор final для класса, а
    • предотвращает прямое создание экземпляров - не предоставляет конструкторов и скрывает или отключает любые неявные конструкторы или конструкторы по умолчанию (и конструкторы копирования), а
    • предоставляет определенные средства для получения экземпляр - статический метод (часто getInstance () ), который возвращает единственный экземпляр или один из ограниченного числа экземпляров
1
ответ дан 29 November 2019 в 21:41
поделиться

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

В остальном это просто вопрос использования self :: $ somevar в ваших методах, если вы делаете его абстрактным, а не $ this-> somevar, если вы реализуете его как синглтон.

0
ответ дан 29 November 2019 в 21:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: