前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP设计模式——迭代模式

PHP设计模式——迭代模式

作者头像
Lemon黄
发布2019-11-13 11:32:20
6660
发布2019-11-13 11:32:20
举报
文章被收录于专栏:Lemon黄Lemon黄

提供一种在不暴露对象内部逻辑的情况下顺序访问聚合对象的元素的方法。

作为商店经理,Eric的工作包括检修各个部门的产品。商店中实际上有两个部门。一个部门称为男士部门,该部门存放男性用品,另一个部门称为女士部门的女性用品。每个部门使用不同的存储结构(数据结构)存储其产品。

男士部门如下所示使用数组(array)结构存储产品:

代码语言:javascript
复制
class MenDepartment
{
    private $_products = array();
    public function  getProducts()
    {
        return $this->_products;
    }
 
    public function addProduct(Product $product)
    {
        $this->_products[] = $product;
    }
}

女士部门如下所示使用SqlStack结构存储产品:

代码语言:javascript
复制
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()

代码语言:javascript
复制
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的接口:

代码语言:javascript
复制
interface ProductIterator
{
   public function hasNext();
   public function next();
}

ProductIterator接口定义StoreManager类将用于迭代产品的两种方法。

让我们为MenDepartment类创建一个具体的迭代器:

代码语言:javascript
复制
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类创建一个具体的迭代器:

代码语言:javascript
复制
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;
   }
}

现在,我们需要为MenDepartmentWomenDepartment创建一个新方法(createIterator)。该方法的作用是实例化先前设计的具体迭代器。这样我们就可以直接获得其迭代器:

代码语言:javascript
复制
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()以使代码更简洁:

代码语言:javascript
复制
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具有抗脆弱性。它与部门用于存储产品的数据结构无关。该代码现在更易于维护。

在我们的示例中,迭代器模式提供了一种顺序访问聚合对象(MenDepartmentWomenDepartment对象)的元素(产品)的方法,而无需暴露其内部逻辑表现形式(ArraySqlStack)。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Lemon黄 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档