2

我试图弄清楚在面向对象的 PHP 中使用迭代器类而不是标准数组的实际好处是什么。

我计划通过将所有数组转换为对象来升级我的框架,但除了让系统完全面向对象之外,我只是不了解实际需求。

我知道通过使用 IteratorAggregate 我可以创建:

class MyModel implements IteratorAggregate {

    public $records = array();

    public function __construct(array $records) {

        $this->records = $records;

    }

    public function getIterator() {

        return new ArrayIterator($this->records);

    }

}

然后像使用数组一样简单地循环遍历它:

$mdlMy = new MyModel(array(
    array('first_name' => 'Mark', 'last_name' => 'Smith'),
    array('first_name' => 'John', 'last_name' => 'Simpson')
));



foreach($mdlMy as $row) {

    echo $row['first_name'];
    echo $row['last_name'];

}

有人可以简单地解释这些的实际目的 - 也许有一些用例。

4

2 回答 2

1

Shortest Answer

Extensibility & abstraction.

Abstract Answer

As soon as you have the ArrayAccess interface, you've got things that aren't arrays but have an array interface. How will you traverse these? You could do it directly, which is where the Iterator interface comes from. Iterator might not make sense for some classes, either due to the single-responsibility principle, or for performance's sake, which is where you get IteratorAggregate.

SPL-based Answer

SPL introduced a number of data structures. Iterators allow these to be traversed in foreach loops. Without iterators, collections would need to be converted to arrays, a potentially costly operation.

Long Answer

Source-Iterators

The first use comes up with data sources (e.g. collections), which aren't all natively held in arrays. Examples (note: there is some overlap):

  • trees
  • the file system
  • the previously mentioned SPL data structures
  • network communications
  • database query results
  • external process results
  • ongoing computation (PHP 5.5 introduces generators for this case)

Any collection that isn't array-based typically either is an iterator or has a corresponding iterator. Without iterators, each of the above would need to be converted to or collected in an array, which might incur heavy time & space costs. If you only had arrays available for iteration, the process can't proceed until the conversion/collection finishes. Iterators allow for partial results to be processed as they become available, and for only portions of collections to be in memory at any point in time.

In particular case outlined in the question, the UserEntityManager::getAll() method could benefit from an Iterator by reducing memory usage. Depending on what is used for data storage, an Iterator will allow just some user records to be processed at a time, rather than loading all at once.

ArrayIterator, DirectoryIterator, and the SPL data structures are all examples of source-iterators (i.e. they iterate over a data source).

Processing-Iterators

Another use for iterators is in-place data processing. Processing iterators wrap other iterators, which allows for iterator composition. In PHP, these are the OuterIterators and sometimes have 'IteratorIterator' in their names.

You might ask "Why not just use functions?" The answer is that you could, but iterator composition (like function composition) is another (powerful) tool that allows for different types of solutions, sometimes achieving better performance or clarity. In particular, functions become a choke point in PHP, since it doesn't have in-language concurrency. Functions must finish before returning a result, which can be costly in terms of time & space, just as using arrays for iteration can be costly. Shallow.

The choke-point could be side-stepped by returning an iterator from a function, but that places a function call in between each iterator. Iterator composition allows deep iterator-based computations, cutting out the middle-man.

As for use-cases, consider a batch processing system that consumes data from multiple feeds, all of which have different formats. An adapting iterator can normalize the data for processing, allowing a single batch processor to service all the feeds.

As a reality check, in PHP you typically don't go full iterator-style any more than you'd write full-FP style, though PHP supports it. You usually don't compose more than a few iterators at a time (just as you often don't compose more than a few functions at a time in languages with function composition), and you don't create numerous iterators instead of functions.

RecursiveIteratorIterator is an example of a processing iterator; it linearizes a tree (simplifying tree traversal).

Iterator & Functional Styles

Iterator composition allows for a style closer to functional programming. At its most basic, an iterator is (roughly) a sequence. In FP, the most basic operation is fold (aka reduce), though others (especially append/concat, filter and map) are often implemented natively rather than in terms of fold for performance. PHP supports a few sequence operations on iterators (usually as OuterIterators); many are missing, but are easy to implement.

  • append: AppendIterator
  • cons: nothing, but easily (though not efficiently) implemented by creating an iterator that takes a single value, converting it to a single-element sequence, along with AppendIterator. EmptyIterator represents the empty sequence.
  • filter: CallbackFilterIterator
  • convolute (aka zip): MultipleIterator
  • slice: LimitIterator
  • map - nothing, but easily implemented
  • fold: nothing. Using a foreach loop and accumulating a value in a variable is probably clearer than implementing fold, but if you find a reason to do so, it's also straightforward (though probably not as an iterator).
  • flat-map: nothing. Can be written fairly easily (though not efficiently) in terms of append and map.
  • cycle: InfiniteIterator
  • unfold: generators (which are, in general, just a special case of iterators).
  • memoization: CachingIterator. Not so much a sequence operation as an (FP language) feature for function results.

The Future

Part of any language design is considering what the language could be. If concurrency were ever added to PHP, code that uses iterators (especially processing-iterators) could be made concurrent without being changed by making the iterators themselves concurrent.

于 2014-11-29T09:12:26.547 回答
0

这样你就没有优势了。然而。但是一旦你需要实现一些新功能,你就会很开心!

如果您想向您的“数组”添加一条记录,但需要先进行一些检查,那么您将在所有情况下都拥有:您不必找到使用此数组的所有位置,但您只需将您的验证添加到构造函数。

模型中可能包含的所有其他内容将成为您的优势。

但请注意,“OOP”不是“我正在使用对象我!”。它还与您拥有的对象、它们的功能等有关。因此,不要只是将所有数组打包在对象中并称其为“完全 OOP”。这与 OOP 无关,在 OOP 项目中使用数组也没有限制。

于 2013-08-27T09:47:48.467 回答