Подтвердить что ты не робот

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

В соответствии с руководство по PHP, класс вроде этого:

abstract class Example {}

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

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

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

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

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

К сожалению, это было более или менее точно повторение моего вопроса. В чем преимущество использования одноэлементного шаблона (a.k.a. hiding __construct()) по сравнению с просто объявлением его abstract и не нужно беспокоиться об этом? (Как, например, это сильная коннотация между разработчиками, что классы abstract фактически не используются или так).

4b9b3361

Ответ 1

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

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

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


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

  • Использование singleton означает использование экземпляра класса; упрощает преобразование не-singleton-класса в одноэлементный: нужно только сделать __construct и __clone private и добавить некоторый метод getInstance.
  • Использование singleton также означает, что у вас есть доступ ко всему, что вы можете использовать с обычным экземпляром: $this, properties,...
  • О, третий (не уверен в этом, но может иметь значение): с 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());

Будет работать... Но это не кажется вполне естественным... и это очень простой пример (см. то, что я сказал ранее с помощью Late Static Binding и магических методов).

Ответ 2

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

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

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

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

class Registry
{
  private function __construct() { }
}

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

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

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

  • Придерживайтесь шаблона singleton и убедитесь, что конструктор также final, поэтому никто не может расширить ваш класс и изменить конструктор на не-private:

    class Registry
    {
      private final function __construct() {
      }
    }
    
  • Сделайте поддержку реестра как статическими, так и объектами:

    class Registry
    {
      protected static $reg = null;
    
      public static function getInstance() {
        if (self::$reg === null) {
          self::$reg = new Registry();
        }
        return self::$reg;
      }
    }
    

    Затем вы можете вызвать Registry::getInstance() статически, или вы можете вызвать new Registry(), если вы хотите экземпляр объекта.

    Затем вы можете делать отличные вещи, например, хранить новый экземпляр реестра в своем глобальном реестре!: -)

    Я реализовал это как часть Zend Framework, в Zend_Registry

Ответ 3

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

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

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

class Registry {
    private $_objects = array( );

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

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

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

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

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

Ответ 9

Из моего понимания, класс без экземпляра - это то, что вы не должны использовать в программе OOP, потому что целая (и единственная) цель классов - служить чертежами для новых объектов. Единственная разница между Registry::$someValue и $GLOBALS['Registry_someValue'] заключается в том, что первый выглядит "fancier", но ни один из способов не является объектно-ориентированным.

Итак, чтобы ответить на ваш вопрос, вам не нужен "singleton class", вам нужен одноэлементный объект, опционально оснащенный методом factory:

class Registry
{
    static $obj = null;

    protected function __construct() {
        ...
    }

    static function object() {
        return self::$obj ? self::$obj : self::$obj = new self;
    }
}

...

Registry::object()->someValue;

Очевидно, что abstract здесь не работает.

Ответ 10

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

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