感谢,bitbird对我的指导
PHP Iterator
PHP 迭代器
作者: Dejan Bosanac
译者: detrox
PHP arrays are generally a very powerful object container. But still, we can easily add a little more fuel to them. Imagine an iterator object as a kind of wrapper around our arrays. What we will try to accomplish here is to create unique interface for traversing arrays and to add a little more control over how our objects are created and finally, to support lazy loading.
PHP数组一般地说是一个十分强大的对象容器。但我们仍然可以给他来点涡轮增压。把迭代器对象设想为一种数组的包装。我们要在这里试着完成的是为遍历数组和在如何创建对象上加入一点额外的控制去创建一个唯一的接口,最后去支持傻瓜式的读取。
Interface
接口
Iterator has a very simple and many times seen interface.
迭代器有一个简单的和经常被看到的接口
<?php
function Iterator($array) // Constructor. Takes array to be traversed as a parameter. 构造函数。使需要遍历的数组作为一个参数
function reset() // Sets internal pointer to the first element 设置内部指针指向第一个元素
function end() // Sets internal pointer to the last element 设置内部指针指向最后一个元素
function seek($position) // Sets internal pointer to desired position 设置内部指针指向一个指定的元素
function next() // Returns next element in the iteration 返回后一个元素
function previous() // Returns previous element in the iteration 返回前一个元素
?>
With the interface like this you can easily perform all your daily tasks (such as traversing arrays) in any way you want and from any position you want. One advantage of using this approach against native PHP array functions is that you have one interface for all of your array tasks. You will not use foreach() construct in one case, list-each combination in other and yet next() and prev() functions in third any more. One more advantage is that now you can easily position on any particular element and start traversing from there (in any way you want). Here are few code examples:
有了这样的接口,我们就可以在任何时间,任何地点,用我们喜欢的任何方法轻轻松松执行我们的日常事务(像数组遍历)。使用这个东西相对于PHP本身的数组函数的一个优势就是你有了一个为你的所有数组任务[工作]的接口。你就用不着在一段代码里用foreach()构造,另外一段用list和each的组合,然后还有别的地方一会儿用到next(),一会儿用到perv()。另一个优势是现在你可以简单的定位于一个确定的元素并且可以从这里开始遍历(以你想要得任何方式). 这里有一些例子:
<?php
// $array = ?. // initialize the array 初始化数组
$iterator = new Iterator($array);
// traverse the array 遍历数组
while ($elem = $iterator->next()) {
echo $elem;
}
// traverse array in reverse order 反序便利
$iterator->end();
while ($elem = $iterator->previous()) {
echo $elem;
}
// traverse array from fifth element on 从第五元素开始遍历
$iterator->seek(5);
while ($elem = $iterator->next()) {
echo $elem;
}
?>
OK, you say, this is all nice but there is nothing I can't do with combination of native PHP functions. Besides the fact that you are accessing all your arrays through unique interface, another (and most important) advantage is that Iterator's object structure allows you to easily expand its functionality.
OK, 你说,这是很不错,但是没有什么我不能使用原始的PHP函数组合来完成的。除了这个事实,你能通过唯一的接口存取你的所有数组,另外(也是最重要的)优势是迭代器的对象结构容许你轻松的扩展它的功能
ObjectIterator interface
对像迭代器器接口
Often I have ended up in situations where my object methods had to return an array of some other object as a result. Usually that object is loaded from the database, but could be some other situation such as obtaining objects through some RPC protocol (XML-RPC, SOAP, ...) or endless other situation combinations that you experience every day. In this article we will focus on the first problem and briefly explain how to empower Iterator for the purpose you'd need.
我经常中断于我的对象方法要返回一些其他对象的数组作为结果[的情况]。通常这钟对象是从数据库读取得,但是也可能是其他状况比如获得一个通过一些RPC 协议 (XML-RPC, SOAP,...) 得到的对象或是你每天要经历的无尽的其他情况的组合。在这篇文章中,我们将聚焦在第一个问题并且简要的解释如何去为你的目的实现迭代器。
Suppose that you are developing an address book for some large web application. Your address book will work with companies and persons. In addition, companies could have an endless number of employees (that are also kinds of persons). So far we have recognized two objects in our application: Company and Person. Also, it is clear that the company will have method getEmployees() that returns an array of Person objects. There are a number of possible implementations of this method. Here are some usual implementations:
假设你在为一个大型web应用程序开发一个通讯录。你的通讯录将工作在公司和人上。另外,公司可能有无穷多的雇员(这也是一类人)。现在我们认清了应用程序中的两个对象: 公司和个人。同时也清楚了Company将有一个方法getEmployees()返回一个人对象的数组。这个方法的实现有很多。这里是一些常规的实现方法:
First, you could write a query to collect all the ids of all the company employees. Then you could make an array that contains all the objects and returns this array. This would look something like this (supposing you have a database
首先,你可以写一个查询去收集所有公司员工的id。然后,你能制造一个数组包含所有的对象再返回这个数组。这看上去像这样(假设你有一个数据库)
<?php
function getEmployees() {
$query = "SELECT id FROM persons WHERE companyid = $this->companyid";
$stmt = execute_query($query); // creates statement object 创建语句对象
$this->employees = array();
while ($row = fetch_object($stmt) {
$this->employess[$row->id] = new Person($row->id); // object creation 对象创建
}
return $this->employees;
}
?>
and the usage would be:
使用起来像是这样:
<?php
$company = new Company("Datagate");
$employees = $company->getEmployees();
foreach ($employees as $id =>$employee)
$employee->addVacationDays(3); // object usage 对象的使用
?>
OK, these look like fairly obvious solutions. But, it has few big flaws. All objects are created but we don't know if we're going to use them all. There are two performance problems here. First accessing a relational database (for creating these objects) can be very time expensive. And if the company has 500 employees and you need to access data for only 50, that is lot of time wasted. Imagine now, that we are loading these objects through RPC which is even slower. This could seriously affect application performance. Now, even if all objects are needed we don't need them at the same time, we need objects one by one as we are traversing the array. The solution above is a huge waste of resources (memory and time).
OK, 这些看上去像是清晰明确的解决方案。但是,它存在一些大的瑕疵。所有的对象都被创建但我们却不知道他们是否都要被使用。这里有两个性能的问题。首先存取一个关系数据库(为了创建这些对象)时间开销很大。其次,如果公司有500个员工并且你只需要为其中50个存取数据,这将是极大的时间上的浪费。现在想象一下,我们将这些对象通过更缓慢的RPC读取。这将严重的影响应用程序的性能。现在,即使所有的对象都是被需要的我们也不是在同一时间需要他们,我们需要一个接着一个的[处理]对象就像我们在遍历一个数组。上面的解方案是一个巨大的资源(内存和时间)浪费。
The solution to these performance problems looks so obvious. Let's return just an array of employee ids. The code would look something like this:
对于这个性能问题的解决方案看起来是那么显而易见。让我们来仅仅返回一个包含员工id的数组。代码看上去像是这样的:
<?php
function getEmployees() {
$query = "SELECT id FROM persons WHERE companyid = $this->companyid";
$stmt = execute_query($query); // creates statement object
$this->employees = array();
while ($row = fetch_object($stmt) {
$this->employess[$row->id] = $row->id;
}
return $this->employees;
}
?>
and the usage would be:
使用就是这样:
<?php
$company = new Company("Datagate");
$employees = $company->getEmployees();
foreach ($employees as $id) {
$employee = new Employee($id); // object creation
$employee->addVacationDays(3); // object usage
}
?>
This looks fine at the first sight. We have saved time and memory , but another problem has arisen. Suppose that the code for creating Employee object changes, for example you need to add extra argument to the constructor or some extra initialization (these are things that are happening on real projects). In that case you'll need to modify your code in many places (wherever you have used getEmployees() method), And that is a problem.
第一眼看上去挺不错。我们节省了时间和内存,但是另一个问题出现了。假设为创建员工对象的代码发生了变化,例如你需要给构造函数加入额外的参数或者一些额外的初始化(这是真实的项目中将会发生的)。在这个问题上,你将要在很多地方修改你的代码(任何你使用getEmployees()方法的地方),这是个问题。
The third solution is to use an ObjectIterator class that is extended from Iterator. In this example we will see how easy it is to extend Iterator class to serve your purposes. When you are using ObjectIterator your getEmployee() function will stay the same as in second solution. So, we have saved our resources. No unnecessary objects are created and everything looks just fine. Now let's look at the usage code:
第三个解决方案是使用一个对象迭代器类,它迭代器的扩展。在这个例子里我们将看到扩展迭代器为你的目的服务是何等容易。当你在使用对象迭代器时你的 getEmployee()[应该是getEmplyees()吧]函数将保持和第二个解决方案一样。因此,我们节省了我们的资源。没有不需要的对象被创建而且每件事看上去都很好。现在,让我们看看使用代码:
<?php
$company = new Company("Datagate");
$iterator = new Iterator($company->getEmployees(), "Employee");
while ($employee = $iterator->next()) {
$employee->addVacationDays(3);
}
?>
We see now that the object creation code is hidden in the ObjectIterator class, so it is now easy to support changes.
我们现在看到了对象创建的代码被隐藏在对象迭代器类里,因此现在很容易去支持改变。
ObjectIterator implementation
对象迭代器的实现
The code for ObjectIterator is quite simple.
对象迭代器的代码十分简单
<?php
/**
* Implements iterator for traversing collection of objects
* for the given array od object identifiers
*
* @version 1.0
* @author <a href=mailto:chubrilo@yahoo.com>Dejan Bosanac</a>
*/
class ObjectIterator extends Iterator {
var $_objectName;
/**
* Constructor
* Calls initialization method (@see Iterator::_initialize())
* and do some specific configuration
* @param array $array array of object ids to be traversed
* @param string $objectName class of objects to be created
* @access public
*
* 构造函数
* 调用初始化方法(参考Iterator::_initialize())
* 做一些特殊的配置
* @参数数组 $array 要遍历的对象id的数组
* @参数字符串 $objectName 要被创建的对象的类
*/
function ObjectIterator($array, $objectName) {
$this->_initialize($array);
$this->_objectName = $objectName;
}
/**
* Returns object with given id
* @param Object $id identifier of the object
* @return Object next object in the collection
* @access private
*
* 用给出的id返回对象
* @参数对象 $id 标示一个对象
* @返回对象 集合里的下一个对象
* @存取 私有
*/
function _fetchObject($id) {
return new $this->_objectName($id);
}
}
?>
It has $_objectName class member that represent class of the object that has to be returned by next() and previous() methods. The constructor sets this internal variable and calls an initialization function (defined in Iterator class). The most important thing is the _fetchObject() function. It encapsulates code for object creation. It is called from next() and previous() methods and takes object id as a parameter. So all your object creation code is localized here, thus making it easy to change and expand.
类成员$_objectName代表对象所属的类它必须被next()和pervious()方法返回。构造函数设置内部变量并且调用初始化函数(已经在迭代器类定义了)。最重要的事情是_fetchObject()函数。他封装了对象创建的代码。它被next()和pervious()方法调用并且用对象的id作为参数)。这样一来你所有的对象创建代码都在这里了,因此他被制作的容易改变和扩展
So, here are instructions for creating our new type of iterators. First, make your constructor (which has to have an array as a parameter) which calls _initialize() function from Iterator class. Second, override _fetchObject method to do whatever you have to do to make your objects. And, that would be all.
这里是我们创建新的一类迭代器的方法。第一,制作你的构造函数(它有一个数组作为参数),它调用从迭代器类_initialize()函数。第二,重载 _fetchObject方法去做任何你必须对你的对象做的。这就是全部。
In conclusion
结论
Iterator will not slow down your application in a way that it will need new hardware to run it. It has some overhead, but for that price you get clean and easy readable code that is flexible enough for future software enhancements.
迭代器不会以任何方式减慢你的应用程序除非它需要一套新的硬件去运行。它有一些负荷,但是给了你清晰易读的代码,这将使今后软件的扩展十分方便