Статические классы в PHP через абстрактное ключевое слово? - PullRequest
21 голосов
/ 22 марта 2010

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

abstract class Example {}

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

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

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

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

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

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

Ответы [ 10 ]

19 голосов
/ 22 марта 2010

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

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

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


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

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

3 голосов
/ 17 апреля 2010

То, что вы описываете, разрешено языком 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
}

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

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

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

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

    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

1 голос
/ 21 апреля 2010

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

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

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

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

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

1 голос
/ 22 марта 2010

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

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

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

class Registry {
    private $_objects = array( );

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

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

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

0 голосов
/ 20 апреля 2010

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

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

0 голосов
/ 20 апреля 2010

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

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

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

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

Таким образом, синглтон кажется лучшим вариантом.

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

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

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

Правильный путь здесь определенно состоит в том, чтобы не использовать Singleton или абстрактный класс, а вместо этого использовать внедрение зависимостей. Самый быстрый способ - получить тонну глобальных или абстрактных классов.

0 голосов
/ 22 марта 2010

abstract действительно предназначен для обозначения «схемы», как вы ее называете, для наследования классов.

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

0 голосов
/ 22 марта 2010

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

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

...