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)將附加職責(牛奶調味品,糖調味品和奶油調味品)動態地附加到對象(咖啡對象)上。裝飾器為子類提供了靈活的替代方案,以擴展功能。