PHP設計模式——迭代模式
- 2019 年 11 月 13 日
- 筆記
提供一種在不暴露對象內部邏輯的情況下順序訪問聚合對象的元素的方法。
作為商店經理,Eric的工作包括檢修各個部門的產品。商店中實際上有兩個部門。一個部門稱為男士部門,該部門存放男性用品,另一個部門稱為女士部門的女性用品。每個部門使用不同的存儲結構(數據結構)存儲其產品。
男士部門如下所示使用數組(array
)結構存儲產品:
class MenDepartment { private $_products = array(); public function getProducts() { return $this->_products; } public function addProduct(Product $product) { $this->_products[] = $product; } }
女士部門如下所示使用SqlStack
結構存儲產品:
class WomenDepartment { private $_products; public function __construct() { $this->_products = new SplStack(); } public function getProducts() { return $this->_products; } public function addProduct(Product $product) { $this->_products->push($product); } }
因此,對於商店經理Eric來說,檢修的程式碼看起來就像以下一樣。注意函數checkIn()
:
class StoreManager { private $_menDepartment; private $_womenDepartment; public function __construct(MenDepartment $menDepartment, WomenDepartm ent $womenDepartment) { $this->_menDepartment = $menDepartment; $this->_womenDepartment = $womenDepartment; } public function checkIn() { $menProducts = $this->_menDepartment->getProducts(); foreach($menProducts as $menProduct) { echo $menProduct->getName(); } $womenProducts = $this->_womenDepartment->getProducts(); while (!$womenProducts->isEmpty()) { $womanProduct = $womenProducts->pop(); echo $womanProduct->getName(); } } }
根據部門使用的數據結構,我們需要使用不同的方式來迭代存儲。這將很快引入一個問題。想像一下,如果女士部門也決定使用數組來存儲產品。我們不僅需要更新WomenDepartment
類,而且還需要更改checkIn()
方法。這顯然違反了單一責任原則(SRP
)。這是因為類(class
)只有一個改變的理由。這裡的checkIn()
方法在很大程度上取決於兩個部門使用的數據結構。至少有兩個原因需要更改。
如果我們可以隱藏部門使用的存儲產品的數據結構,並提供一個迭代產品的通用方法,該會怎樣?這時就是我們需要迭代器模式(Iterator Pattern
)的時候。
讓我們重新調整程式碼。
首先,我們需要創建一個稱為迭代器的Iterator
的介面:
interface ProductIterator { public function hasNext(); public function next(); }
ProductIterator
介面定義StoreManager
類將用於迭代產品的兩種方法。
讓我們為MenDepartment
類創建一個具體的迭代器:
class MenDepartmentIterator implements ProductIterator { private $_position = 0; private $_products = array(); public function __construct($products) { $this->_products = $products; } public function hasNext() { return ($this->_position < count($this->_products)); } public function next() { $product = $this->_products[$this->_position]; $this->_position ++; return $product; } }
我們在MenDepartment
中需要一個內部指針來跟蹤當前索引。
同理,我們也為WomenDepartment
類創建一個具體的迭代器:
class WomenDepartmentIterator implements ProductIterator { private $_products ; public function __construct($products) { $this->_products = $products; } public function hasNext() { return !($this->_products->isEmpty()); } public function next() { $product = $this->_products->pop(); return $product; } }
現在,我們需要為MenDepartment
和WomenDepartment
創建一個新方法(createIterator
)。該方法的作用是實例化先前設計的具體迭代器。這樣我們就可以直接獲得其迭代器:
class MenDepartment { private $_products = array(); public function getProducts() { return $this->_products; } public function addProduct(Product $product) { $this->_products[] = $product; } public function createIterator() { return new MenDepartmentIterator($this->_products); } } class WomenDepartment { private $_products; public function __construct() { $this->_products = new SplStack(); } public function getProducts() { return $this->_products; } public function addProduct(Product $product) { $this->_products->push($product); } public function createIterator() { return new WomenDepartmentIterator($this->_products); } }
最後,讓我們看看StoreManager
的變化。其餘的都保持不變,我們唯一需要更改的地方是checkIn()
方法,我們還需要添加一個便捷方法checkInByIterator()
以使程式碼更簡潔:
class StoreManager { private $_menDepartment; private $_womenDepartment; public function __construct(MenDepartment $menDepartment, WomenDepartment $womenDepartment) { $this->_menDepartment = $menDepartment; $this->_womenDepartment = $womenDepartment; } public function checkIn() { $menDepartmentIterator = $this->_menDepartment->createIterator(); $womenDepartmentIterator = $this->_womenDepartment->createIterator(); $this->checkInByIterator($menDepartmentIterator); $this->checkInByIterator($womenDepartmentIterator); } public function checkInByIterator(ProductIterator $productIterator) { while ($productIterator->hasNext()) { $product = $productIterator->next(); echo $product->getName(); } } }
正如我們最後所看到的,StoreManager
具有抗脆弱性。它與部門用於存儲產品的數據結構無關。該程式碼現在更易於維護。
在我們的示例中,迭代器模式提供了一種順序訪問聚合對象(MenDepartment
和WomenDepartment
對象)的元素(產品)的方法,而無需暴露其內部邏輯表現形式(Array
或SqlStack
)。