【设计模式】抽象工厂模式

引入

抽象工厂(Abstract Factory)模式是比工厂模式更高一层的抽象。抽象工厂模式的核心思想是通过一个超级工厂来创建其它工厂,超级工厂又称为其它工厂的工厂。

超级工厂是负责创建一个相关对象的工厂,并且不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

如何工作

假设我们需要 编写一个程序来规划一块植物园的设计,这有可能是一年生的植物园、菜园和多年生的植物园。不论是哪一种园,我们都需要考虑一下几个问题:

  • 哪些植物适宜种在边上?
  • 哪些植物适宜种在中间?
  • 哪些植物适宜种在阴凉处?

当然还有许多其它问题,我们在这不一一列出。

现在,我们可以创建一个Garden类作为一年生植物园、菜园、多年生植物园的基类,Garden类通过 showPlant 方法来回答这些问题。

然后创建一个 Plant 类,这个类包含了植物名称等相关的信息。

我们继续创建 VeggieGarden(菜园) 类、PerennialGarden (多年生植物园)类,他们分别继承自 Garden类。

20170423162913

然后创建GardenMaker超级工厂类,高级工厂类可以通过 garden_type 返回 AnnualGarden 和 VeggieGarden 其中一个类的实例。

示例代码

首先是 Plant (植物)类:

  1. <?php
  2.  
  3. /**
  4.  *  植物
  5.  * Created by PhpStorm.
  6.  * User: AppDays
  7.  * Date: 2017/4/23
  8.  * Time: 14:36
  9.  */
  10. class Plant
  11. {
  12.     private $name;
  13.  
  14.     /**
  15.      * Plant constructor.
  16.      * @param $name 植物名称
  17.      */
  18.     public function __construct($name)
  19.     {
  20.         $this->name=$name;
  21.     }
  22.  
  23.     /**
  24.      * Desc: 返回植物名称
  25.      * User: AppDays
  26.      * Time: 2017-4-23 14:41:26
  27.      * @return 植物名称
  28.      */
  29.     public function showName(){
  30.         echo $this->name.'</br>';
  31.     }
  32. }

Plant类,实例化时,传入植物名称;showName方法可以打印出自己的名称。

然后Garden类:

  1. <?php
  2.  
  3. /**
  4.  *  植物园
  5.  * Created by PhpStorm.
  6.  * User: AppDays
  7.  * Date: 2017/4/23
  8.  * Time: 14:42
  9.  */
  10. include_once 'Plant.php';
  11. abstract class Garden
  12. {
  13.     /**
  14.      * @var Plant
  15.      */
  16.     protected  $center,$shade,$border;
  17.     /**
  18.      * @var bool
  19.      */
  20.     protected $showCenter,$showShade,$showBorder;
  21.  
  22.     public function setCenter(){
  23.         $this->showCenter=true;
  24.     }
  25.     public function setShade(){
  26.         $this->showShade=true;
  27.     }
  28.     public function setBorder(){
  29.         $this->showBorder=true;
  30.     }
  31.  
  32.     /**
  33.      * Desc:  显示各个区域的植物名称
  34.      * User: AppDays
  35.      * Time: 2017-4-23 14:53:45
  36.      */
  37.     public function showPlant(){
  38.         if($this->showCenter){
  39.             $this->center->showName();
  40.         }
  41.         if($this->showShade){
  42.             $this->shade->showName();
  43.         }
  44.         if($this->showBorder){
  45.             $this->border->showName();
  46.         }
  47.     }
  48. }

Garden类作为植物园的基类,包含三个Plant类型的属性,分别代表种植在中间的植物、种植在边上的植物、种植在阴凉处的植物。并且可以通过 showPlant方法打印出已经种植的植物。

下面是VeggieGarden(菜园)类:

  1. <?php
  2.  
  3. /**
  4.  *&nbsp; 菜园
  5.  * Created by PhpStorm.
  6.  * User: AppDays
  7.  * Date: 2017/4/23
  8.  * Time: 14:54
  9.  */
  10. include_once 'Garden.php';
  11. include_once 'Plant.php';
  12. class VeggieGarden extends Garden
  13. {
  14.     public function __construct()
  15.     {
  16.         $this->center= new Plant('玉米');
  17.         $this->shade= new Plant("西蓝花");
  18.         $this->border = new Plant("豌豆");
  19.     }
  20.  
  21. }

下面是AnnualGarden (多年生植物园)类:

  1. <?php
  2.  
  3. /**
  4.  *  多年生植物园
  5.  * Created by PhpStorm.
  6.  * User: AppDays
  7.  * Date: 2017/4/23
  8.  * Time: 15:03
  9.  */
  10. include_once 'Garden.php';
  11. include_once 'Plant.php';
  12. class PerennialGarden extends Garden
  13. {
  14.     public function __construct()
  15.     {
  16.         $this->center= new Plant('苹果树');
  17.         $this->shade= new Plant("车前草");
  18.         $this->border = new Plant("灌木丛");
  19.     }
  20. }

VeggieGarden 和 AnnualGarden 作为具体的工厂,可以返回不同的植物对象(或者植物名)。现在我们需要超级工厂类,这个类可以通过植物园类型返回不同的植物园实例,下面是超级工厂类:GardenMaker

  1. <?php
  2.  
  3. /**
  4.  *  工厂生成器
  5.  * Created by PhpStorm.
  6.  * User: AppDays
  7.  * Date: 2017/4/23
  8.  * Time: 15:12
  9.  */
  10. include_once 'PerennialGarden.php';
  11. include_once 'VeggieGarden.php';
  12. class GardenMaker
  13. {
  14.     /*
  15.      * 根据植物园类型返回植物园
  16.      *
  17.      */
  18.     public static function getGarden($gardenType){
  19.         switch($gardenType){
  20.             case 'veggie':
  21.                 return new VeggieGarden();
  22.                 break;
  23.             case 'perennial':
  24.                 return new PerennialGarden();
  25.                 break;
  26.         }
  27.     }
  28. }

GardenMaker 类有一个静态方法: getGarden。这个方法通过传入的植物园类型返回一个具体的植物园工厂实例。通过这个超级工厂类,使用者不需要关心各种植物园工厂类是什么,只需要知道各个植物园类型即可:

  1. <?php
  2. /**
  3.  * Created by PhpStorm.
  4.  * User: AppDays
  5.  * Date: 2017/4/23
  6.  * Time: 15:29
  7.  */
  8. header("Content-type: text/html; charset=utf-8");
  9. include_once 'GardenMaker.php';
  10. //根据菜园类型获取菜园工厂对象
  11. $garden = GardenMaker::getGarden('veggie');
  12. $garden->setBorder();
  13. $garden->setCenter();
  14. $garden->setShade();
  15. //显示种植的植物
  16. $garden->showPlant();
  17. //根据多年生植物园类型获取多年生植物园工厂对象
  18. $garden = GardenMaker::getGarden('perennial');
  19. $garden->setBorder();
  20. $garden->setCenter();
  21. $garden->setShade();
  22. //显示种植的植物
  23. $garden->showPlant();

Ok,浏览器打开运行:

20170423163011

优点

抽象工厂一个非常大的优点就是可以非常容易的添加新的子类。例如,我们还可以添加一块草地、一块花园等等,只需要继承 Garden类即可,对现有代码来说,需要变动的就是添加一些方式来选择这些园地类型。

效果

抽象工厂的主要目的之一就是隔离所生成的具体类,这类类的真正类名被隐藏在超级工厂内部,完全不需要让客户端层面知道。因为类被隔离,所以这些产品类系列可以被随意更改和交换而不会影响客户端层面的处理逻辑。

源码

设计模式系列源码,有更新就会提交到 github/appdays/DesignPattern 上。嗯~~结束。