php设计模式之装饰者模式

拉面的故事

拉面馆里卖拉面,拉面分为小碗和大碗,小碗一份6元,大碗一份9元。另外如果加牛肉的话,则需加6元,加一个鸡蛋是1元,加大排是5元一份,加一块锅巴是1元。如果用传统的写法,需要写8个类(拉面份量数*配菜数)。如果现在面馆新推一种份量——中碗,那么,就需要新增4个类。这样就会造成一个问题——类爆炸。

另外,用传统的继承方法不够灵活,如果用户要点两份锅巴三个蛋这种需求呢。

装饰者模式

装饰模式是一种结构型设计模式, 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。

想要理解装饰者模式,可以想象一个玩偶——套娃

每套一个娃,就相当于添加了一个装饰的对象。在运行时,会运行最外层的装饰对象(取外层的娃),然后一层一层的运行。

现在我们用装饰者模式来解决拉面这个问题。

思考一个问题,为什么这里没有把拉面的份量作为装饰者对象?想想看,你会点一份既是大碗又是小碗的拉面吗?

装饰者模式特点

  • 装饰者和被装饰者对象有相同的超类型
  • 可以用一个或多个装饰者包装一个对象
  • 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。

代码实现

Noodles.php代码如下

<?php

namespace app\decorator;

abstract class Noodles
{
   public abstract function getDesc ();

   public abstract function cost ();
}

NoodlesDecorator.php代码如下

<?php

namespace app\decorator;

abstract class NoodlesDecorator extends Noodles
{

}

BigNoodles.php代码如下:

<?php

namespace app\decorator;

class BigNoodles extends Noodles
{
   private $desc = '大碗';

   public function cost()
  {
       return 9;
  }

   public function getDesc()
  {
       return $this->desc;
  }
}

SmallNoodles.php代码如下:

<?php

namespace app\decorator;

class SmallNoodles extends Noodles
{
   private $desc = '小碗拉面';

   public function cost()
  {
       return 6;
  }

   public function getDesc()
  {
       return $this->desc;
  }
}

Beef.php、Egg.php、Crust.php代码如下:

<?php

namespace app\decorator;


class Beef extends NoodlesDecorator
{
   private $desc = '牛肉';
   private $noodles = null;

   public function __construct(Noodles $noodles)
  {
       $this->noodles = $noodles;
  }

   public function getDesc()
  {
       return $this->noodles->getDesc() . $this->desc;
  }

   public function cost()
  {
       return $this->noodles->cost() + 6;
  }
}

class Egg extends NoodlesDecorator
{
   private $desc = '鸡蛋';
   private $noodles = null;

   public function __construct(Noodles $noodles)
  {
       $this->noodles = $noodles;
  }

   public function getDesc()
  {
       return $this->noodles->getDesc() . $this->desc;
  }

   public function cost()
  {
       return $this->noodles->cost() + 1;
  }
}

class Crust extends NoodlesDecorator
{
   private $desc = '锅巴';
   private $noodles = null;

   public function __construct(Noodles $noodles)
  {
       $this->noodles = $noodles;
  }

   public function getDesc()
  {
       return $this->noodles->getDesc() . $this->desc;
  }

   public function cost()
  {
       return $this->noodles->cost() + 1;
  }
}

测试代码如下:

$noodles = new BigNoodles();
$beefBigNoodles = new Beef($noodles);
$eggBeffBigNoodles = new Egg($beefBigNoodles);

echo $eggBeffBigNoodles->getDesc() . '拉面';
echo $eggBeffBigNoodles->cost() . '元';

将会输出大碗牛肉鸡蛋拉面16元