在先前的文章《深入淺析PHP中的觀察者模式》中我們介紹了PHP中的觀察者模式,以下這篇文章帶大家了解一下PHP中的迭代器模式。
一說到這個模式,就不得不提循環語句。在《大話設計模式》中,作者說這個模式現在的學習意義更大於實際意義,這是為什麼呢?當然就是被foreach這貨給整得。任何語言都有這種類似的語法可以方便快捷的對數組、物件進行遍歷,從而讓迭代器模式從高高在上的23大設計模式中的明星慢慢成為了路人。特別是我們這門PHP語言,PHP的強大之處就在於對於數組的靈活操作,本身就是hashmap的結構,自然會有各種方便的數組操作語法,而foreach也是我們最常用的語句,甚至比for還常用。
GoF定義:提供一個方法順序存取一個聚合物件中各個元素,而無需暴露該物件的內部表示
GoF類別圖
程式碼實作
interface Aggregate { public function CreateIterator(); } class ConcreteAggregate implements Aggregate { public function CreateIterator() { $list = [ "a", "b", "c", "d", ]; return new ConcreteIterator($list); } }
首先是聚合類,也就是可以進行迭代的類,這裡因為我是物件導向的設計模式,所以迭代器模式針對的是對一個類別的內容進行迭代。在這裡,其實我們也只是模擬了一個陣列交給了迭代器。
interface MyIterator { public function First(); public function Next(); public function IsDone(); public function CurrentItem(); } class ConcreteIterator implements MyIterator { private $list; private $index; public function __construct($list) { $this->list = $list; $this->index = 0; } public function First() { $this->index = 0; } public function Next() { $this->index++; } public function IsDone() { return $this->index >= count($this->list); } public function CurrentItem() { return $this->list[$this->index]; } }
迭代器閃亮登場,主要實作了四個方法來對集合資料進行操作。有點像學習資料結構或資料庫時對遊標進行的操作。用First()和Next()來移動遊標,用CurrentItem()來取得目前遊標的資料內容,用IsDone()來確認是否還有下一資料。所以,這個模式也另稱為遊標模式。
$agreegate = new ConcreteAggregate(); $iterator = $agreegate->CreateIterator(); while (!$iterator->IsDone()) { echo $iterator->CurrentItem(), PHP_EOL; $iterator->Next(); }
客戶端直接使用while來進行操作即可。
我們的手機工廠不得了,自己組裝了一條生產線,這條生產線主要是做什麼的呢?成型機我們已經交給富X康來搞定了,我們這條線就是幫手機刷顏色的。當我們把所有已經交貨的手機(Aggregate)放到不同的生產線後(Iterator),就會一台一台的幫我們刷上目前生產線的顏色,是不是很強大! !科技不只換殼,這條線還在,我們就可以再做別的事兒,比如加點掛繩什麼的,反正只要能一台一台的通過我就能裝上東西,你說好用不好用! !
完整程式碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/07.iterator/source/iterator.php
实例还是围绕着我们的短信发送来看。这一次,我们的业务需求是尽快的发一批通知短信给用户,因为活动的时候可不等人啊。在之前我们会使用多个脚本来把用户手机号分成多组来进行发送。现在我们可以用swoole来直接多线程的发送。所要达到的效果其实就是为了快速的把成百上千的短信发完。这个时候我们也会做一些策略,比如数据库里是100条要送的短信,有个字段是发送状态,一个线程正序的发,一个线程倒序的发,当正序和倒序都发送到50条的时候其实已经同步的发完这100条了,不过也有可能会有失败的情况出现,这时,两个线程还会继续去发送那些上次发送不成功的信息,这样能够最大程度的确保发送的效率和到达率。
消息发送迭代器类图
完整源码:https://github.com/zhangyue0503/designpatterns-php/blob/master/07.iterator/source/iterator-msg.php
<?php interface MsgIterator { public function First(); public function Next(); public function IsDone(); public function CurrentItem(); } // 正向迭代器 class MsgIteratorAsc implements MsgIterator { private $list; private $index; public function __construct($list) { $this->list = $list; $this->index = 0; } public function First() { $this->index = 0; } public function Next() { $this->index++; } public function IsDone() { return $this->index >= count($this->list); } public function CurrentItem() { return $this->list[$this->index]; } } // 反向迭代器 class MsgIteratorDesc implements MsgIterator { private $list; private $index; public function __construct($list) { // 反转数组 $this->list = array_reverse($list); $this->index = 0; } public function First() { $this->index = 0; } public function Next() { $this->index++; } public function IsDone() { return $this->index >= count($this->list); } public function CurrentItem() { return $this->list[$this->index]; } } interface Message { public function CreateIterator($list); } class MessageAsc implements Message { public function CreateIterator($list) { return new MsgIteratorAsc($list); } } class MessageDesc implements Message { public function CreateIterator($list) { return new MsgIteratorDesc($list); } } // 要发的短信号码列表 $mobileList = [ '13111111111', '13111111112', '13111111113', '13111111114', '13111111115', '13111111116', '13111111117', '13111111118', ]; // A服务器脚本或使用swoole发送正向的一半 $serverA = new MessageAsc(); $iteratorA = $serverA->CreateIterator($mobileList); while (!$iteratorA->IsDone()) { echo $iteratorA->CurrentItem(), PHP_EOL; $iteratorA->Next(); } // B服务器脚本或使用swoole同步发送反向的一半 $serverB = new MessageDesc(); $iteratorB = $serverB->CreateIterator($mobileList); while (!$iteratorB->IsDone()) { echo $iteratorB->CurrentItem(), PHP_EOL; $iteratorB->Next(); }
说明
完整源码:https://github.com/zhangyue0503/designpatterns-php/blob/master/06.observer/source/spl_observer.php
彩蛋
PHP中的Iterator接口已经为我们准备好了一套标准的Iterator模式的实现,而且(这里需要画重点),实现这个接口的类可以用foreach来遍历哦!
文档:https://www.php.net/manual/zh/class.iterator.php
源码:https://github.com/zhangyue0503/designpatterns-php/blob/master/07.iterator/source/iterator-php.php
文档中相关的接口都可以看看,更重要的是,PHP的SPL扩展中,也为我们准备了很多常用的迭代器封装。要知道,面试的时候要是能说出这里面的几个来,那面试官可是也会刮目相看的哦!
SPL迭代器:https://www.php.net/manual/zh/spl.iterators.php
本文转载自:https://juejin.cn/post/6844903937921777671
作者:硬核项目经理
推荐学习:《PHP视频教程》
以上是淺談PHP中的迭代器模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!