Возможно ли использовать чрезмерное статическое связывание в PHP?

Начиная с версии 5.3, PHP поддерживает последнее связывание для статических методов. Хотя это, несомненно, полезная функция, есть только несколько случаев, когда ее использование действительно необходимо (например, шаблон активной записи).

Рассмотрим следующие примеры:

1. Конструкторы удобства (::create())

class SimpleObject
{
    public function __construct() { /* ... */ }

    public static function create()
    {
        return new static; // or: return new self;
    }
}

Если этот класс может быть расширен (однако он не расширен каким-либо классом в одном пакете), следует использовать позднюю статическую привязку, чтобы упростить ее расширение (без необходимости переписывать метод ::create() и, более того, важно, не задумываясь об этом)?

Примечание. Эта идиома используется, чтобы обойти невозможность вызова методов только что построенных объектов: new SimpleObject()->doStuff() недействителен в PHP.


2. Константы класса

class TagMatcher
{
    const TAG_PATTERN = '/\<([a-z\-]+?)\>/i';

    private $subject;

    public function construct($subject) { $this->subject = $subject; }

    public function getAllTags()
    {
        $pattern = static::TAG_PATTERN;
        preg_match_all($pattern, $this->subject);
        return $pattern[1];
    }
}

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


Итак, чтобы обернуть все это, являются ли эти использования (и подобные) поздней статической привязкой избыточным? Есть ли заметная производительность? Кроме того, частое использование позднего связывания уменьшает общее повышение производительности, предоставляемое кэшами операций операций?

Ответы

Ответ 1

Итак, чтобы обернуть все это, являются ли эти использования (и подобные) поздней статической привязкой избыточным? Есть ли заметная производительность? Кроме того, частое использование позднего связывания уменьшает общее повышение производительности, предоставляемое кэшами операций операций?

Введение позднего статического связывания исправляет недостаток в объектной модели PHP. Это не о производительности, а о семантике.

Например, мне нравится использовать статические методы всякий раз, когда реализация метода не использует $this. Просто потому, что метод статичен, это не означает, что вы не хотите иногда его переопределять. До PHP 5.3 поведение состояло в том, что ошибка была отмечена, если вы переопределили статический метод, но PHP просто продолжал и молча использовал родительскую версию. Например, приведенный ниже код печатает "A" перед PHP 5.3. Это очень неожиданное поведение.

Последнее статическое связывание исправляет его, и теперь тот же код печатает "B".

<?php
class A {
  public static function who() {
    echo __CLASS__;
  }
  public static function test() {
    static::who();
  }
}

class B extends A {
  public static function who() {
    echo __CLASS__;
  }
}

B::test();
?>

Ответ 2

статические методы (ранние или поздние) создают плотную связь и (таким образом) уменьшают возможность тестирования. вы можете создавать большие программы на PHP без использования нескольких статических вызовов. для меня поздние статические методы не являются функциями.

изменить, чтобы ответить на вопрос Marco Demaio, как статический метод уменьшает возможности тестирования?

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

Скажем, у вас есть веб-приложение, использующее базу данных SQL. ваши бизнес-объекты могут извлекать данные, используя статический интерфейс или полиморфизм. либо

class MyBusinessObject
extends...
{
  public function doThisOrThat(...)
  {
    $results = db::query('sql string...');
    ...
  }
}

или

class MyBusinessObject
extends...
{
  public function __construct(dbconn $db)
  {
    $this->db = $db;
  }
  private $db;
  public function doThisOrThat(...)
  {
    $results = $this->db->query('sql string...');
    ...
  }
}

последнее проще тестировать (как в: я хочу проверить, что строка sql, построенная из таких-то и подобных входов, является такой-то), потому что проще создать другую реализацию интерфейса dbconn, чем он должен изменить значение db::. почему вы тоже хотите? потому что вам не нужна настоящая база данных для проверки поведения sql-составления, и на самом деле ее легче протестировать без реальной базы данных. Кроме того, проще заглушить пользователя sql, если ваши тесты связаны с другим аспектом CUT (Code Under Test).

тестирование всегда подразумевает ложь на проверенный код о его сотрудниках, а воздержание от статических интерфейсов ( "doublecolon" или "quadridot" ) означает, что ложь не должна быть масштабной хирургией, что является плюсом, поскольку чем дальше тестируется код из производственного кода, тем менее значимыми являются результаты тестирования.

Ответ 3

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

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