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
)。