­

PHP设计模式——装饰器模式

  • 2019 年 11 月 14 日
  • 筆記

动态地给对象附加职责。装饰器为子类扩展功能提供了非常灵活的替代方案。

咖啡深受人们的喜爱。咖啡的香气、味道和能力增强足以开启人们美好的一天。这就难怪为什么星巴克和咖啡豆这样的咖啡店在生意上可以做得这么好。

一杯不含奶油或牛奶的纯咖啡,其代表可以如下所示:

interface Beverage  {      public function getName();  }    class Coffee implements Beverage  {      public function getName()      {          return 'Coffee';      }  }

但是,纯咖啡由于具有极强的苦味,因此并不是真正的流行选择。大多数人喜欢在咖啡中加牛奶。这不仅可以减轻咖啡的浓烈味道,还可以使咖啡散发出甜美醇厚的味道。

要在代码中创建它,我们通常会使用创建子类。继承似乎是很自然的选择:

class CoffeeWithMilk extends Coffee  {      public function getName()      {          return 'Coffee with milk';      }  }

现在,我们已经在咖啡中添加了牛奶,我们意识到这并不是我们在饮料中添加的唯一添加物。有些人喜欢糖,有些人喜欢奶油和糖,有些人想要牛奶和糖。如果我们需要创建所有可能的品种怎么办?假设奶油,牛奶和糖是三种可用的调味品,那么有六种可能的方法来煮一杯咖啡。对于每种咖啡变体,我们将需要创建一个子类:

class CoffeeWithMilk extends Coffee {}  class CoffeeWithCream extends Coffee {}  class CoffeeWithSugar extends Coffee {}  class CoffeeWithMilkAndCream extends Coffee {}  class CoffeeWithMilkAndSugar extends Coffee {}  class CoffeeWithCreamAndSugar extends Coffee {}

实际上,还有更多的变体。选择可能很简单,例如减少牛奶或减少糖分,但是需要注意这些变化的影响。现在,当您考虑添加可可粉,肉桂和其他可增强咖啡风味的添加剂时,这可能是一场噩梦。

我们将需要更好的解决方案。这时装饰者模式就是派上用场的时候。我们将创建装饰器类,向咖啡对象添加调味品。首先,让我们创建一个将牛奶添加到咖啡中的装饰器类:

class WithMilkDecorator implements Beverage  {      private $_coffee = null;        public function __construct(Coffee $coffee)      {          $this->_coffee = $coffee;      }        public function getName()      {          return $this->_coffee->getName().' with milk';      }  }

注意,WithMilkDecorator类仍然像Coffee类一样实现Beverage接口。

让我们来创建另外两个装饰器:

class WithSugarDecorator implements Beverage  {      private $_coffee = null;      public function __construct(Coffee $coffee)      {          $this->_coffee = $coffee;      }        public function getName()      {          return $this->_coffee->getName().' with sugar';      }  }    class WithCreamDecorator implements Beverage  {      private $_coffee = null;      public function __construct(Coffee $coffee)      {          $this->_coffee = $coffee;      }        public function getName()      {          return $this->_coffee->getName().' with cream';      }  }

现在,我们将不再拥有六个Coffee子类,而将拥有三个装饰器,它们将实现我们所需的相同目标。例如,要用牛奶和糖煮一杯咖啡:

$coffee = new Coffee();  $coffeeWithMilk = new WithMilkDecorator($coffee);  $coffeeWithMilkAndSugar = new WithSugarDecorator($coffeeWithMilk);  echo $coffeeWithMilkAndSugar->getName();

显然,通过使用装饰器模式(Decorator Pattern),我们创建了更少的类来进行维护,并避免引入带有更多代码的错误。而且,装饰器模式(Decorator Pattern)在运行时向包装类添加了附加功能,因此与通过子类进行继承相比,添加/删除功能很灵活。

在我们的示例中,装饰器模式(Decorator Pattern)将附加职责(牛奶调味品,糖调味品和奶油调味品)动态地附加到对象(咖啡对象)上。装饰器为子类提供了灵活的替代方案,以扩展功能。