【设计模式】工厂方法模式

引入

之前介绍过简单工厂模式(Simle Factory),它由某个类作为指挥者,根据传入的类型决定它某个继承层次中的子类将被实例化。

工厂方法(Factory Method)模式是简单工厂模式的一个扩展。在工厂方法模式中,不会由单个类来做出实例化哪一个子类的决定,超类会把这一决定推迟到每一个子类中。该模式实际上并不存在一个决策点。

工厂方法如何工作

我们可以用一个简单的降序和升序的例子来演示工厂方法。我们来看一下下面的类图:

  uml_factory_method

和上次的简单工厂模式比,工厂方法模式中,工厂类会有多个类型(AscSortFactory、DescSortFactory),并且都继承自同一个超类(SortFactory),每个工厂子类都会返回一个确定类型的“产品实例”(AscSorter、DescSorter)。

使用者需要降序排序时,只需要实例化一个DescSortFactory 工厂类,通过 getSorter 方法获取一个排序器(DescSorter),使用者不需要知道 AscSorter 和 DescSorter 的实现及区别,只需要知道 可以通过排序器的超类 Sorter 的 getArray 方法获取排序结果就可以。

示例代码

首先是抽象工厂类,所有工厂类都必须继承这个类。

  1. <?php
  2.  
  3. /**
  4.  * Created by PhpStorm.
  5.  * User: AppDays
  6.  * Date: 2017/3/26
  7.  * Time: 19:14
  8.  */
  9. abstract class SortFactory
  10. {
  11.     protected $array;
  12.     function __construct($array){
  13.         $this->array=$array;
  14.     }
  15.  
  16.     /**
  17.      * Desc: 子类必须实现的方法,返回一个排序对象
  18.      * User: AppDays
  19.      * Time: 2017-3-26 19:20:29
  20.      * @return sorter
  21.      */
  22.    protected abstract function getSorter();
  23.  
  24.     /**
  25.      * Desc: 子类必须实现的方法,返回是否是升序排序
  26.      * User: AppDays
  27.      * Time: 2017-3-26 19:23:18
  28.      * @return bool
  29.      */
  30.    protected abstract function isASC();
  31.  
  32.     /**
  33.      * Desc: 子类必须实现的方法,返回是否是降序排序
  34.      * User: AppDays
  35.      * Time: 2017-3-26 19:30:16
  36.      * @return bool
  37.      */
  38.     protected abstract function isDESC();
  39. }

然后是抽象“产品类”,所有“产品”(排序器)都必须继承这个类。

  1. <?php
  2.  
  3. /**
  4.  * Created by PhpStorm.
  5.  * User: AppDays
  6.  * Date: 2017/3/26
  7.  * Time: 19:44
  8.  */
  9. abstract class Sorter
  10. {
  11.     //排序后得到的数组
  12.     private $array;
  13.  
  14.     /**
  15.      * Sorter constructor.
  16.      * @param $array 待排序数组
  17.      */
  18.     function __construct($array)
  19.     {
  20.         $this->array=$this->sorting($array);
  21.  
  22.     }
  23.  
  24.     /**
  25.      * Desc: 所有子类都有的方法,获取排序后的数组,子类无需实现
  26.      * User: AppDays
  27.      * Time: 2017-3-26 19:47:12
  28.      * @return array
  29.      */
  30.     public  function getArray(){
  31.         return $this->array;
  32.     }
  33.  
  34.     /**
  35.      * Desc: 所有子类必须实现的方法,用来对数组进行排序,并返回排序后的数组
  36.      * User: AppDays
  37.      * Time: 2017-3-26 19:51:33
  38.      * @param $array
  39.      * @return array
  40.      */
  41.     protected abstract function sorting($array);
  42. }

下面是降序排序器,继承自 Sorter 类。

  1. <?php
  2.  
  3. /**
  4.  * Created by PhpStorm.
  5.  * User: AppDays
  6.  * Date: 2017/3/26
  7.  * Time: 19:56
  8.  */
  9. include_once "Sorter.php";
  10. class DescSorter extends Sorter
  11. {
  12.     /**
  13.      * AscSorter constructor.
  14.      * @param $array 待排序数组
  15.      */
  16.     public function __construct($array)
  17.     {
  18.         parent::__construct($array);
  19.     }
  20.  
  21.     /**
  22.      * Desc: 实现父类排序方法,降序排序,使用快速排序算法
  23.      * User: AppDays
  24.      * Time: 2017-3-26 20:01:31
  25.      * @param $array
  26.      * @return array
  27.      */
  28.     protected function sorting($array)
  29.     {
  30.         if(is_array($array) == false){
  31.             return array($array);
  32.         }
  33.         $length =count($array);
  34.         if($length <= 1){
  35.             return $array;
  36.         }
  37.         $leftArr=array();
  38.         $rightArr=array();
  39.         for($i=1;$i<$length;$i++){
  40.             if($array[$i]>$array[0]){
  41.                 $leftArr[]=$array[$i];
  42.             }else{
  43.                 $rightArr[]=$array[$i];
  44.             }
  45.         }
  46.         $leftArr=$this->sorting($leftArr);
  47.         $rightArr=$this->sorting($rightArr);
  48.         return array_merge($leftArr,array($array[0]),$rightArr);
  49.     }
  50.  
  51.  
  52. }

下面是升序排序器,同样继承自 Sorter 类,同样使用快速排序算法。

  1. <?php
  2.  
  3. /**
  4.  * Created by PhpStorm.
  5.  * User: AppDays
  6.  * Date: 2017/3/26
  7.  * Time: 20:16
  8.  */
  9. include_once "Sorter.php";
  10. class AscSorter extends Sorter
  11. {
  12.     /**
  13.      * AscSorter constructor.
  14.      * @param $array 待排序数组
  15.      */
  16.     public function __construct($array)
  17.     {
  18.         parent::__construct($array);
  19.     }
  20.  
  21.     /**
  22.      * Desc: 实现父类排序方法,升序排序,使用快速排序算法
  23.      * User: AppDays
  24.      * Time: 2017-3-26 20:20:31
  25.      * @param $array
  26.      * @return array
  27.      */
  28.     protected function sorting($array)
  29.     {
  30.         if(is_array($array) == false){
  31.             return array($array);
  32.         }
  33.         $length =count($array);
  34.         if($length <= 1){
  35.             return $array;
  36.         }
  37.         $leftArr=array();
  38.         $rightArr=array();
  39.         for($i=1;$i<$length;$i++){
  40.             if($array[$i]<$array[0]){
  41.                 $leftArr[]=$array[$i];
  42.             }else{
  43.                 $rightArr[]=$array[$i];
  44.             }
  45.         }
  46.         $leftArr=$this->sorting($leftArr);
  47.         $rightArr=$this->sorting($rightArr);
  48.         return array_merge($leftArr,array($array[0]),$rightArr);
  49.     }
  50.  
  51.  
  52. }

有了产品,我们就开始造工厂,下面的是降序排序工厂。

  1. <?php
  2.  
  3. /**
  4.  * Created by PhpStorm.
  5.  * User: AppDays
  6.  * Date: 2017/3/26
  7.  * Time: 20:36
  8.  */
  9. include_once "SortFactory.php";
  10. include_once "DescSorter.php";
  11. class DescSortFactory extends SortFactory
  12. {
  13.  
  14.     /**
  15.      * AscSortFactory constructor.
  16.      * @param $array 待排序数组
  17.      */
  18.     public function __construct($array)
  19.     {
  20.         parent::__construct($array);
  21.     }
  22.  
  23.     /**
  24.      * Desc: 实现父类获取排序器的方法,这里返回一个降序排序器的实例
  25.      * User: AppDays
  26.      * Time: 2017-3-26 20:42:06
  27.      * @return AscSorter
  28.      */
  29.     public function getSorter()
  30.     {
  31.         return new DescSorter($this->array);
  32.         // TODO: Implement getSorter() method.
  33.     }
  34.  
  35.     /**
  36.      * Desc: 实现父类方法,返回是否是升序工厂
  37.      * User: AppDays
  38.      * Time: 2017-3-26 20:47:45
  39.      * @return bool
  40.      */
  41.     public function isASC()
  42.     {
  43.         return false;
  44.         // TODO: Implement isASC() method.
  45.     }
  46.  
  47.     /**
  48.      * Desc: 实现父类方法,返回是否是降序工厂
  49.      * User: AppDays
  50.      * Time: 2017-3-26 20:50:06
  51.      * @return bool
  52.      */
  53.     public function isDESC()
  54.     {
  55.         return true;
  56.         // TODO: Implement isASC() method.
  57.     }
  58. }

升序排序工厂,同样继承自 SortFactory 类。

  1. <?php
  2.  
  3. /**
  4.  * Created by PhpStorm.
  5.  * User: AppDays
  6.  * Date: 2017/3/26
  7.  * Time: 20:55
  8.  */
  9. include_once "SortFactory.php";
  10. include_once "AscSorter.php";
  11. class AscSortFactory extends SortFactory
  12. {
  13.  
  14.     /**
  15.      * AscSortFactory constructor.
  16.      * @param $array 待排序数组
  17.      */
  18.     public function __construct($array)
  19.     {
  20.         parent::__construct($array);
  21.     }
  22.  
  23.     /**
  24.      * Desc: 实现父类获取排序器的方法,这里返回一个升序排序器的实例
  25.      * User: AppDays
  26.      * Time: 2017-3-26 20:59:08
  27.      * @return AscSorter
  28.      */
  29.     public function getSorter()
  30.     {
  31.         return new AscSorter($this->array);
  32.         // TODO: Implement getSorter() method.
  33.     }
  34.  
  35.     /**
  36.      * Desc: 实现父类获取排序器的方法,返回是否是升序工厂
  37.      * User: AppDays
  38.      * Time: 2017-3-26 21:04:23
  39.      * @return bool
  40.      */
  41.     public function isASC()
  42.     {
  43.         return true;
  44.         // TODO: Implement isASC() method.
  45.     }
  46.  
  47.     /**
  48.      * Desc: 实现父类获取排序器的方法,返回是否是降序工厂
  49.      * User: AppDays
  50.      * Time: 2017-3-26 21:07:56
  51.      * @return bool
  52.      */
  53.     public function isDESC()
  54.     {
  55.         return false;
  56.         // TODO: Implement isASC() method.
  57.     }
  58. }

使用工厂

和上次一样,新建一个index.php 文件,分别创建降序工厂和升序工厂,然后对一个乱序的数组进行排序测试:

  1. <?php
  2. /**
  3.  * Created by PhpStorm.
  4.  * User: AppDays
  5.  * Date: 2017/3/26
  6.  * Time: 21:15
  7.  */
  8. include "AscSortFactory.php";
  9. include "DescSortFactory.php";
  10. //待排序数组
  11. $array=array(3,9,8,4,1,10);
  12. //升序排序工厂
  13. $factory = new AscSortFactory($array);
  14. //通过工厂获取一个升序排序器
  15. $sorter= $factory->getSorter();
  16. //通过排序器获取排序结果
  17. $arr=$sorter->getArray();
  18. print_r($arr);
  19. echo '</br>';
  20. //降序排序工厂
  21. $factory = new DescSortFactory($array);
  22. //通过工厂获取一个降序排序器
  23. $sorter= $factory->getSorter();
  24. //通过排序器获取排序结果
  25. $arr=$sorter->getArray();
  26. print_r($arr);

测试结果:

image

什么时候使用工厂方法

当然,这里使用简单的排序场景来演示工厂方法有点高射炮打蚊子的嫌疑,下面是教科书的原文,什么情况下考虑使用工厂方法?

  • 类不能预测它必须创建哪一种类型的对象。
  • 类使用它的子类来指定它要创建的对象。
  • 希望只有局部知晓哪个类会被创建。