Domanda

A partire dalla versione 5.3, PHP supporta late binding per metodi statici. Sebbene sia una funzionalità indubbiamente utile, ci sono solo diversi casi in cui il suo uso è davvero necessario (ad esempio il modello Active Record).

Considera questi esempi:

1. Costruttori di convenienza ( :: create () )

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

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

Se questa classe può essere estesa (tuttavia, non è estesa da nessuna classe nello stesso pacchetto), si dovrebbe usare l'associazione statica tardiva solo per rendere più semplice l'estensione (senza dover riscrivere :: create () e, soprattutto, senza dover ricordare di farlo)?

Nota: questo idioma viene usato per aggirare l'impossibilità di chiamare metodi su oggetti appena costruiti: new SimpleObject () - > doStuff () non è valido in PHP.


2. Costanti di classe

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];
    }
}

Il motivo per usare static :: in questo esempio è simile al precedente. È usato solo perché questa classe può essere creata per abbinare tag di diversa forma semplicemente estendendola e sovrascrivendo la costante.


Quindi, per concludere, questi usi (e simili) del binding statico tardivo sono eccessivi? C'è qualche notevole impatto sulle prestazioni? Inoltre, l'uso frequente dell'associazione tardiva riduce l'incremento complessivo delle prestazioni fornito dalle cache degli opcode?

È stato utile?

Soluzione

  

Quindi, per concludere, questi usi (e simili) del binding statico tardivo sono eccessivi? C'è qualche notevole impatto sulle prestazioni? Inoltre, l'uso frequente dell'associazione tardiva riduce l'incremento complessivo delle prestazioni fornito dalle cache degli opcode?

L'introduzione dell'associazione statica tardiva corregge un difetto nel modello a oggetti di PHP. Non si tratta di prestazioni, si tratta di semantica.

Ad esempio, mi piace usare metodi statici ogni volta che l'implementazione del metodo non usa $ this . Solo perché un metodo è statico non significa che a volte non si desidera sovrascriverlo. Prima di PHP 5.3, il comportamento era che nessun errore veniva segnalato se si superava un metodo statico, ma PHP andava avanti e utilizzava silenziosamente la versione del genitore. Ad esempio, il codice seguente stampa "A" prima di PHP 5.3. È un comportamento molto inaspettato.

Il binding statico tardivo lo risolve e ora lo stesso codice stampa '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();
?>

Altri suggerimenti

i metodi statici (in anticipo o in ritardo) creano un accoppiamento stretto e (quindi) riducono la testabilità. puoi creare programmi di grandi dimensioni in PHP senza usare più di alcune chiamate statiche. per me, i metodi statici in ritardo non sono una funzionalità.

modifica per rispondere alla domanda di Marco Demaio, in che modo il metodo statico riduce la testabilità?

Mi dispiace se questo è ovvio per te, i membri statici (sia dati che metodi) sono utili e non fanno male se usati responsabilmente, alludevo al loro uso improprio prevalente.

afferma di avere un'applicazione Web che utilizza un database SQL. i tuoi oggetti business possono recuperare dati usando un'interfaccia statica o attraverso il polimorfismo. o

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

o

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

quest'ultimo è più facile da testare (come in: voglio testare che la stringa sql costruita da tali input è tale e così) perché è più facile creare un'altra implementazione di dbconn piuttosto che cambiare il significato di db :: . perché vorresti? perché non è necessario un vero database per testare il comportamento di composizione di sql, ed infatti è più facile testare senza un vero database. inoltre, è più facile eliminare il consumatore sql se i tuoi test riguardano un altro aspetto del CUT (Code Under Test).

test implica sempre mentire al codice testato sui suoi collaboratori e astenersi da interfacce statiche (il "doublecolon" o "quadridot") significa che la menzogna non deve essere un intervento chirurgico massiccio, il che è un vantaggio, poiché più lontano il codice testato proviene dal codice di produzione, meno significativi sono i risultati del test.

Dove trovo la necessità di utilizzare l'associazione statica tardiva è quello di consentire derisione di metodi statici per test unitari con PHPUnit. Il problema che ho è che non mi piace modificare il codice rigorosamente per consentire il derisione, ma posso superarlo.

Per rispondere alla tua domanda, tuttavia, scommetterei che qualunque costo di prestazione comporta, impallidirà rispetto alla maggior parte dei tempi di esecuzione del programma. In altre parole, non farà alcuna differenza evidente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top