PHP 迭代器 (轉)

gugu99發表於2007-12-05
PHP 迭代器 (轉)[@more@]

感謝,bitbird對我的指導 


Iterator
PHP 迭代器


作者: Dejan Bosanac
譯者: detrox

PHP arrays are generally a very powerful 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.

迭代器有一個簡單的和經常被看到的介面

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 perfoall 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()。另一個優勢是現在你可以簡單的定位於一個確定的元素並且可以從這裡開始遍歷(以你想要得任何方式). 這裡有一些例子:

// $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. Bess 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 (-RPC, P, ...) 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 develo an address book for some large 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。然後,你能製造一個陣列包含所有的物件再返回這個陣列。這看上去像這樣(假設你有一個資料庫)

function getEmployees() {
$query = " 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:

使用起來像是這樣:

$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 res (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的陣列。程式碼看上去像是這樣的:

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:

使用就是這樣:

$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()吧]函式將保持和第二個解決方案一樣。因此,我們節省了我們的資源。沒有不需要的物件被建立而且每件事看上去都很好。現在,讓我們看看使用程式碼:

$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.
物件迭代器的程式碼十分簡單

/**
* Implements iterator for traversing collection of objects
* for the given array od object identifiers
*
* @version 1.0
* @author Dejan Bosanac
*/
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.

迭代器不會以任何方式減慢你的應用程式除非它需要一套新的去執行。它有一些負荷,但是給了你清晰易讀的程式碼,這將使今後的擴充套件十分方便

.yu/~chuki//iterator.tar.gz">你可以在這裡源程式


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-988575/,如需轉載,請註明出處,否則將追究法律責任。

相關文章