PHP设计模式——复合模式

2019-11-13 22:36:03 浏览数 (1)

使你可以将对象组合到树结构中,以表示部分整体层次结构。复合可以使客户端统一对待单个对象和对象组成。

Agnes在沃尔玛工作了一年多;她一开始担任的是库存助理,最近被提升为库存业务员。Agnes作为库存业务员的主要工作是进行每日库存记录。在上班的第三天,经理很担心地与Agnes接触。“Agnes,你记录的玩具车库存不准确,并不是所有的盒子里面都有相同数量的汽车。这是因为有些玩具车比其他玩具车大,所以盒子里装的玩具车就少了。”向经理道歉后,Agnes重新上班了。她是这项工作的新手,她意识到这项工作比她想象的要难,她不得不想出一种准确记录箱子的方法。

以下是产品(Product)类代码表示的样子:

代码语言:javascript复制
class Product
{
    private $_name = null;
    public function  __construct($name)
    {
        $this->_name = $name;
    }
 
    public function getName()
    {
        echo $this->_name;
    }
}

以下是箱子(Box)类代码表示的样子:

代码语言:javascript复制
class Box
{
   private $_products = array();
   public function addProduct($products)
   {
       $this->_products = $products;
   }
 
   public function getProducts()
   {
       echo $this->_product;
   }
}

以下是Agnes工作的代码表示:

代码语言:javascript复制
class InventoryClerk
{
    public function recordProducts(Box $box)
    {
        $products = $box->getProducts();
        foreach($products as $product) {
            $product->getName();
        }
    }
}

整个库存过程似乎非常简单。但是,事实并非如此。在大多数情况下,一个大盒子由十几个小盒子组成,在某些情况下,产品是用一个小盒子包装的。对于Agnes而言,这是一项艰巨的任务,并且通常需要花费更多的时间,因为她必须将所有内容整理好,并根据包装盒的内容和产品数量为包装盒创建不同的类别。这使她的工作比我应有的繁琐。

解决此问题的一种可能方法是在代码上放置一些条件。但是,产品的包装可能会在将来发生变化,并且无法说明即将推出的产品的实际内容。这使得这个可能的解决方案不切实际,并且代码难以维护。

Agnes需要更好的解决方案,一个易于维护的可持续解决方案。

这就是复合模式(Composite Pattern)适合应用的时候。在我们的案例中,对象是一个整体层次结构。盒子可能包含盒子或产品。我们可以使InventoryClerk类使用复合模式(Composite Pattern)统一处理Product对象和Box对象。

在复合模式中。有四个要素:

  • Component(组件):这是既要实现Leaf又要实现Composite的接口。此类定义客户端使用的抽象功能。在我们的例子中,函数是getName()
  • Leaf:顾名思义,它是没有子类的类。它实现了如上所述的Component接口。在我们的例子中,Leaf是我们的产品Product 类。
  • Composite:复合抽象的Component类。因此,除了实现Component接口之外,它还可以复合具体的Product类,Composite类或两者。
  • Client(客户端):它通过调用Component类中定义的通用函数来统一处理Leaf类和Composite类。

下面,我们来重构以上的类。首先,我们需要一个产品组件接口ProductComponent

代码语言:javascript复制
interface ProductComponent
{
   public function getName();
}

重构产品Product 类,它要实现刚刚定义的产品组件接口ProductComponent

代码语言:javascript复制
class Product implements ProductComponent
{
    private $_name = null;
 
    public function  __construct($name)
    {
        $this->_name = $name;
    }
 
    public function getName()
    {
        echo $this->_name;
    }
}

重构箱子Box类,除了实现ProductComponent接口之外,它还需要具有添加,删除和获取ProductComponent的功能:

代码语言:javascript复制
class Box implements ProductComponent
{
   private $_productComponents = array();
   public function getName()
   {
        $productComponents = $this->getProductComponents();
        foreach($productComponents as $productComponent) {
             $productComponent->getName();
        }
   }
 
   public function addProductComponent(ProductComponent $productComponent)
   {
        $this->_productComponents[] = $productComponent;
   }
 
   public function removeProductComponent($i)
   {
        unset($this->_productComponents[$i]);
   }
 
   public function getProductComponents()
   {
        return $this->_productComponents;
   }
}

最后,重构库存检验类InventoryClerk;它将变得非常简洁:

代码语言:javascript复制
class InventoryClerk
 {
    public function recordProducts(ProductComponent $productComponent)
    {
        $productComponent->getName();
    }
}

现在,我们已经将复合模式Composite Pattern应用于我们的案例,我们的代码变得更加易于维护,并且也很灵活。无论一个盒子中有多少个盒子,或者无论这些盒子的大小和内容是否不同,我们的代码都可以使用。

让我们以两个玩具产品分别包装在一个小盒子中为例,然后将两个小盒子放入一个大盒子中:

代码语言:javascript复制
$productHelicopter = new Product('toy helicopter');
$productCar       = new Product('toy car');
$smallBoxForHelicopter = new Box();
$smallBoxForHelicopter->addProductComponent($productHelicopter);
$smallBoxForCar = new Box();
$smallBoxForCar->addProductComponent($productCar);
$bigBox = new Box();
$bigBox->addProductComponent($smallBoxForHelicopter);
$bigBox->addProductComponent($smallBoxForCar);

对于库存检查,要记录大箱子,就像以下这么简单:

代码语言:javascript复制
class InventoryClerk
{
    public function recordProducts(ProductComponent $productComponent)
    {
        $productComponent->getName();
    }
}

在我们的示例中,“复合模式(Composite Pattern)”使我们能够将对象(产品Product对象和Box对象)组合为树形结构,以表示部分整体层次结构。复合(Composite)使客户端(InventoryClerk对象)可以对单个对象(Product对象和Box对象)和对象组成进行统一处理(通过ProductComponent抽象)。

0 人点赞