2012-11-29 3 views
9

Die IteratorAggregate ist eine Schnittstelle eine externe Iterator zu erstellen:PHP-Schnittstellen IteratorAggregate vs Iterator?

class myData implements IteratorAggregate 
{ 
    public $property1 = "Public property one"; 
    public $property2 = "Public property two"; 
    public $property3 = "Public property three"; 

    public function __construct() 
    { 
     $this->property4 = "last property"; 
    } 

    public function getIterator() 
    { 
     return new ArrayIterator($this); 
    } 
} 

$obj = new myData; 

Und Sie werden in der Lage zu durchqueren das Objekt mit foreach:

foreach($obj as $key => $value) { 
    var_dump($key, $value); 
    echo "\n"; 
} 

Während Iterator eine Schnittstelle für externe Iteratoren oder Objekte, die intern selbst iteriert werden können:

class myIterator implements Iterator 
{ 
    private $position = 0; 
    private $array = array('one', 'two', 'three'); 

    function rewind() 
    { 
     $this->position = 0; 
    } 

    function current() 
    { 
     return $this->array[$this->position]; 
    } 

    function key() 
    { 
     return $this->position; 
    } 

    function next() 
    { 
     ++$this->position; 
    } 

    function valid() 
    { 
     return isset($this->array[$this->position]); 
    } 
} 

Und wieder, können Sie es im Grunde die gleiche Art und Weise verfahren:

$it = new myIterator; 

foreach($it as $key => $value) { 
    var_dump($key, $value); 
    echo "\n"; 
} 

kann also jemand erklären, warum wir zwei Schnittstellen benötigen, und was ist der Unterschied zwischen ihnen?

Antwort

12

Ich nehme an, IteratorAggregate ist für Fälle, in denen Sie Zeit sparen möchten, und Iterator ist für Fälle, in denen Sie feine Kontrolle über Iteration durch Ihr Objekt benötigen. Zum Beispiel können Sie benutzerdefinierte Ausnahmen für next(), key() oder prev() Fehler hinzufügen, Zwischenspeichern (wenn Sie durch etwas durchlaufen, die Daten über das Netzwerk nimmt), verarbeiten Sie die Werte vor der Rückgabe.

+0

Ich denke, Sie sind falsch mit den benutzerdefinierten Methoden. – tacone

+0

Und wenn Sie noch mehr Zeit sparen wollen, können Sie einfach die Werte von 'getIterator' übernehmen. – Bell

6

IteratorAggregate ist eine einfache Möglichkeit, einen Iterator zu implementieren. Der Nachteil ist, dass Sie nicht hinzufügen next(), key(), etc. Methoden, wie sie nicht während einer normalen foreach Traversal aufgerufen werden können. Wenn Sie benutzerdefinierte Methoden implementieren müssen, müssen Sie eine OuterIterator implementieren oder (einfacher) eine IteratorIterator erweitern. Der Vorteil mit IteratorAggregate ist die Geschwindigkeit, es ist viel schneller als seine Alternativen. Das Problem damit ist, dass ... trotz des Namens ist es kein Iterator, sondern ein traversable (also wieder, keine nächsten, Schlüssel, aktuelle, gültige, Zurückspulen Methoden).

Hier ist eine einfache Benchmark ich für jeden von IteratorAggregate, IteratorIterator und OuterIterator mit beiden iterator_to_array und foreach mit 1 Million Iterationen auf meinen armen Laptop laufen: (auch die echo innerhalb next Methode der Aggregate beachten: nein ‚x‘ wird gedruckt, der Nachweis wird nie aufgerufen)

$ repeat 3 php benchIterator.php 
------------------------------------ 
Outer_ToArray: 569.61703300476 ms 
Aggregate_ToArray: 552.38103866577 ms 
IteratorIt_ToArray: 546.95200920105 ms 
Outer_Foreach: 1679.8989772797 ms 
IteratorIt_Foreach: 1019.6850299835 ms 
Aggregate_Foreach: 367.35391616821 ms 
------------------------------------ 
Outer_ToArray: 570.75309753418 ms 
Aggregate_ToArray: 544.40784454346 ms 
IteratorIt_ToArray: 555.06300926208 ms 
Outer_Foreach: 1682.2130680084 ms 
IteratorIt_Foreach: 988.21592330933 ms 
Aggregate_Foreach: 356.41598701477 ms 
------------------------------------ 
Outer_ToArray: 566.06101989746 ms 
Aggregate_ToArray: 543.1981086731 ms 
IteratorIt_ToArray: 546.28610610962 ms 
Outer_Foreach: 1663.2289886475 ms 
IteratorIt_Foreach: 995.28503417969 ms 
Aggregate_Foreach: 356.16087913513 ms 

Hier ist der Code, das ich für die Benchmark verwendet:

<?php 

class Aggregate implements \IteratorAggregate 
{ 
    protected $var; 

    public function __construct($var = null) 
    { 
     if (is_array($var)) { 
      $this->var = new ArrayIterator($var); 
     } 
     if ($var instanceof \Traversable) { 
      $this->var = $var; 
     } 
    } 
    public function next() 
    { 
     echo 'x'; 
    } 
    public function toArray() 
    { 
     return iterator_to_array($this->var, true); 
    } 

    public function getIterator() 
    { 
     return $this->var; 
    } 

} 

class Outer implements \OuterIterator 
{ 
    protected $var; 

    public function __construct($var = null) 
    { 
     if (is_array($var)) { 
      $this->var = new ArrayIterator($var); 
     } 
     if ($var instanceof \Traversable) { 
      $this->var = $var; 
     } 
    } 

    public function toArray() 
    { 
     return iterator_to_array($this->var, true); 
    } 

    public function getInnerIterator() 
    { 
     return $this->var; 
    } 

    public function current() 
    { 
     return $this->var->current(); 
    } 
    public function next() 
    { 
     $this->var->next(); 
    } 
    public function key() 
    { 
     return $this->var->key(); 
    } 
    public function valid() 
    { 
     return $this->var->valid(); 

    } 
    public function rewind() 
    { 
    $this->var->rewind(); 

    } 
} 


class IteratorIt extends IteratorIterator 
{ 
    public function __construct($var = null) 
    { 
     if (is_array($var)) { 
      $var = new ArrayIterator($var); 

     } 
     parent::__construct($var); 

    } 

    public function toArray() 
    { 
     return iterator_to_array($this->getInnerIterator(), true); 
    } 
    public function getIterator() 
    { 
     return $this->getInnerIterator(); 
    } 
} 

function bench($name, $test) 
{ 
    echo "$name: "; 
    $start = microtime(true); 
    $test(); 
    $time = microtime(true); 
    $time -= $start; 

    echo ($time * 1000) . ' ms' . PHP_EOL; 
} 

$max = 1e6; 
$array = range (1, 1e6); 
$testSuites = [ 
    'Outer_ToArray' => function() use ($max, $array) { 
     $iterator = new Outer($array); 
     $r = $iterator->toArray(); 
    }, 
    'Aggregate_ToArray' => function() use ($max, $array) { 
     $iterator = new Aggregate($array); 
     $r = $iterator->toArray(); 
    }, 
    'IteratorIt_ToArray' => function() use ($max, $array) { 
     $iterator = new IteratorIt($array); 
     $r = $iterator->toArray(); 
    }, 
    'Outer_Foreach' => function() use ($max, $array) { 
     $iterator = new Outer($array); 
     foreach ($iterator as $k => $v) 
     { 

     } 
    }, 
    'IteratorIt_Foreach' => function() use ($max, $array) { 
     $iterator = new IteratorIt($array); 
     foreach ($iterator as $k => $v) 
     { 

     } 
    }, 
    'Aggregate_Foreach' => function() use ($max, $array) { 
     $iterator = new Aggregate($array); 
     foreach ($iterator as $k => $v) 
     { 

     } 
    }, 
]; 

echo '------------------------------------'.PHP_EOL; 
foreach ($testSuites as $name => $test) { 
    bench($name, $test); 
} 
3

Was ist der Unterschied zwischen ihnen?

Der Unterschied in Namen ("Aggregate").

Was bedeutet Aggregat?

Aggregatstatus garantiert die Konsistenz von Änderungen, die innerhalb des Aggregats vorgenommen werden, indem externen Objekten untersagt wird, Referenzen auf ihre Mitglieder zu halten. (wikipedia)


Der TC Code:

class myData implements IteratorAggregate {//your implementation} 

class myIterator implements Iterator {//your implementation} 

Hier ist der Code für die Klarheit der Aggregate:

$renderFunction = function($iterator) { 
    foreach($iterator as $key => $value) { 
     echo "$key: $value\n"; 
     foreach($iterator as $key => $value) { 
      echo " $key: $value\n"; 
     } 
    } 
}; 

$renderFunction(new myData); // Aggregate on 
$renderFunction(new myIterator); // Aggregate off 

Was Sie in den Fällen zu erwarten?

https://3v4l.org/JHDNQ

property1: Public property one 
    property1: Public property one 
    property2: Public property two 
    property3: Public property three 
    property4: last property 
property2: Public property two 
    property1: Public property one 
    property2: Public property two 
    property3: Public property three 
    property4: last property 
property3: Public property three 
    property1: Public property one 
    property2: Public property two 
    property3: Public property three 
    property4: last property 
property4: last property 
    property1: Public property one 
    property2: Public property two 
    property3: Public property three 
    property4: last property 

vs

0: one 
    0: one 
    1: two 
    2: three 
+0

Ich bin neugierig, in der Verbindung, die Sie zur Verfügung stellen, sieht es aus wie php7 ist langsamer als php 5.6 zum Beispiel. Ist das normal? oder ich verstehe die Daten überhaupt nicht – Dazag

+0

@Dazag Sie haben Recht, aber es ist wenig diff von Zeit und ich weiß nicht, ist dort verwendet Opcache .... –

2

Durch IteratorAggregate Umsetzung delegieren wir die Aufgabe Iterator Funktionen der Implementierung (key(), next(), current(), valid(), rewind()) zu anderen Klasse (nur Implementierung getIterator()

Auf diese Weise hilft es uns, Probleme in OOP zu trennen.